Fix KVM import unmanaged instances on basic zone (#8465)

This PR fixes import unmanaged instances on KVM basic zones, on top of #8433

Fixes: #8439: point 1
This commit is contained in:
Nicolas Vazquez 2024-01-10 04:51:00 -03:00 committed by GitHub
parent c569fe9119
commit b8d3e342be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 144 additions and 16 deletions

View File

@ -20,6 +20,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.cloud.dc.DataCenter;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.ConfigKey.Scope;
@ -338,7 +339,7 @@ public interface NetworkOrchestrationService {
*/
void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile vmProfile, NicProfile nicProfile);
Pair<NicProfile, Integer> importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, boolean forced) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException;
Pair<NicProfile, Integer> importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, final DataCenter datacenter, boolean forced) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException;
void unmanageNics(VirtualMachineProfile vm);
}

View File

@ -4564,18 +4564,18 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
@DB
@Override
public Pair<NicProfile, Integer> importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, final boolean forced)
public Pair<NicProfile, Integer> importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, final DataCenter dataCenter, final boolean forced)
throws ConcurrentOperationException, InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
s_logger.debug("Allocating nic for vm " + vm.getUuid() + " in network " + network + " during import");
String guestIp = null;
IPAddressVO freeIpAddress = null;
if (ipAddresses != null && StringUtils.isNotEmpty(ipAddresses.getIp4Address())) {
if (ipAddresses.getIp4Address().equals("auto")) {
ipAddresses.setIp4Address(null);
}
if (network.getGuestType() != GuestType.L2) {
guestIp = _ipAddrMgr.acquireGuestIpAddress(network, ipAddresses.getIp4Address());
} else {
guestIp = null;
freeIpAddress = getGuestIpForNicImport(network, dataCenter, ipAddresses);
if (freeIpAddress != null && freeIpAddress.getAddress() != null) {
guestIp = freeIpAddress.getAddress().addr();
}
if (guestIp == null && network.getGuestType() != GuestType.L2 && !_networkModel.listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) {
throw new InsufficientVirtualNetworkCapacityException("Unable to acquire Guest IP address for network " + network, DataCenter.class,
@ -4583,6 +4583,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
}
}
final String finalGuestIp = guestIp;
final IPAddressVO freeIp = freeIpAddress;
final NicVO vo = Transaction.execute(new TransactionCallback<NicVO>() {
@Override
public NicVO doInTransaction(TransactionStatus status) {
@ -4594,12 +4595,13 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
NicVO vo = new NicVO(network.getGuruName(), vm.getId(), network.getId(), vm.getType());
vo.setMacAddress(macAddressToPersist);
vo.setAddressFormat(Networks.AddressFormat.Ip4);
if (NetUtils.isValidIp4(finalGuestIp) && StringUtils.isNotEmpty(network.getGateway())) {
Pair<String, String> pair = getNetworkGatewayAndNetmaskForNicImport(network, dataCenter, freeIp);
String gateway = pair.first();
String netmask = pair.second();
if (NetUtils.isValidIp4(finalGuestIp) && StringUtils.isNotEmpty(gateway)) {
vo.setIPv4Address(finalGuestIp);
vo.setIPv4Gateway(network.getGateway());
if (StringUtils.isNotEmpty(network.getCidr())) {
vo.setIPv4Netmask(NetUtils.cidr2Netmask(network.getCidr()));
}
vo.setIPv4Gateway(gateway);
vo.setIPv4Netmask(netmask);
}
vo.setBroadcastUri(network.getBroadcastUri());
vo.setMode(network.getMode());
@ -4636,6 +4638,30 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
return new Pair<NicProfile, Integer>(vmNic, Integer.valueOf(deviceId));
}
protected IPAddressVO getGuestIpForNicImport(Network network, DataCenter dataCenter, Network.IpAddresses ipAddresses) {
if (network.getGuestType() == GuestType.L2) {
return null;
}
if (dataCenter.getNetworkType() == NetworkType.Advanced) {
String guestIpAddress = _ipAddrMgr.acquireGuestIpAddress(network, ipAddresses.getIp4Address());
return _ipAddressDao.findByIp(guestIpAddress);
}
return _ipAddressDao.findBySourceNetworkIdAndDatacenterIdAndState(network.getId(), dataCenter.getId(), IpAddress.State.Free);
}
/**
* Obtain the gateway and netmask for a VM NIC to import
* If the VM to import is on a Basic Zone, then obtain the information from the vlan table instead of the network
*/
protected Pair<String, String> getNetworkGatewayAndNetmaskForNicImport(Network network, DataCenter dataCenter, IPAddressVO freeIp) {
VlanVO vlan = dataCenter.getNetworkType() == NetworkType.Basic && freeIp != null ?
_vlanDao.findById(freeIp.getVlanId()) : null;
String gateway = vlan != null ? vlan.getVlanGateway() : network.getGateway();
String netmask = vlan != null ? vlan.getVlanNetmask() :
(StringUtils.isNotEmpty(network.getCidr()) ? NetUtils.cidr2Netmask(network.getCidr()) : null);
return new Pair<>(gateway, netmask);
}
private String generateNewMacAddressIfForced(Network network, String macAddress, boolean forced) {
if (!forced) {
throw new CloudRuntimeException("NIC with MAC address = " + macAddress + " exists on network with ID = " + network.getId() +

View File

@ -29,6 +29,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.cloud.dc.DataCenter;
import com.cloud.network.IpAddressManager;
import com.cloud.utils.Pair;
import org.apache.log4j.Logger;
import org.junit.Assert;
import org.junit.Before;
@ -125,6 +128,7 @@ public class NetworkOrchestratorTest extends TestCase {
testOrchastrator.routerNetworkDao = mock(RouterNetworkDao.class);
testOrchastrator._vpcMgr = mock(VpcManager.class);
testOrchastrator.routerJoinDao = mock(DomainRouterJoinDao.class);
testOrchastrator._ipAddrMgr = mock(IpAddressManager.class);
DhcpServiceProvider provider = mock(DhcpServiceProvider.class);
Map<Network.Capability, String> capabilities = new HashMap<Network.Capability, String>();
@ -708,4 +712,88 @@ public class NetworkOrchestratorTest extends TestCase {
Assert.assertEquals(ip6Dns[0], profile.getIPv6Dns1());
Assert.assertEquals(ip6Dns[1], profile.getIPv6Dns2());
}
@Test
public void testGetNetworkGatewayAndNetmaskForNicImportAdvancedZone() {
Network network = Mockito.mock(Network.class);
DataCenter dataCenter = Mockito.mock(DataCenter.class);
IPAddressVO ipAddressVO = Mockito.mock(IPAddressVO.class);
String networkGateway = "10.1.1.1";
String networkNetmask = "255.255.255.0";
String networkCidr = "10.1.1.0/24";
Mockito.when(dataCenter.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced);
Mockito.when(network.getGateway()).thenReturn(networkGateway);
Mockito.when(network.getCidr()).thenReturn(networkCidr);
Pair<String, String> pair = testOrchastrator.getNetworkGatewayAndNetmaskForNicImport(network, dataCenter, ipAddressVO);
Assert.assertNotNull(pair);
Assert.assertEquals(networkGateway, pair.first());
Assert.assertEquals(networkNetmask, pair.second());
}
@Test
public void testGetNetworkGatewayAndNetmaskForNicImportBasicZone() {
Network network = Mockito.mock(Network.class);
DataCenter dataCenter = Mockito.mock(DataCenter.class);
IPAddressVO ipAddressVO = Mockito.mock(IPAddressVO.class);
String defaultNetworkGateway = "10.1.1.1";
String defaultNetworkNetmask = "255.255.255.0";
VlanVO vlan = Mockito.mock(VlanVO.class);
Mockito.when(vlan.getVlanGateway()).thenReturn(defaultNetworkGateway);
Mockito.when(vlan.getVlanNetmask()).thenReturn(defaultNetworkNetmask);
Mockito.when(dataCenter.getNetworkType()).thenReturn(DataCenter.NetworkType.Basic);
Mockito.when(ipAddressVO.getVlanId()).thenReturn(1L);
Mockito.when(testOrchastrator._vlanDao.findById(1L)).thenReturn(vlan);
Pair<String, String> pair = testOrchastrator.getNetworkGatewayAndNetmaskForNicImport(network, dataCenter, ipAddressVO);
Assert.assertNotNull(pair);
Assert.assertEquals(defaultNetworkGateway, pair.first());
Assert.assertEquals(defaultNetworkNetmask, pair.second());
}
@Test
public void testGetGuestIpForNicImportL2Network() {
Network network = Mockito.mock(Network.class);
DataCenter dataCenter = Mockito.mock(DataCenter.class);
Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class);
Mockito.when(network.getGuestType()).thenReturn(GuestType.L2);
Assert.assertNull(testOrchastrator.getGuestIpForNicImport(network, dataCenter, ipAddresses));
}
@Test
public void testGetGuestIpForNicImportAdvancedZone() {
Network network = Mockito.mock(Network.class);
DataCenter dataCenter = Mockito.mock(DataCenter.class);
Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class);
Mockito.when(network.getGuestType()).thenReturn(GuestType.Isolated);
Mockito.when(dataCenter.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced);
String ipAddress = "10.1.10.10";
IPAddressVO ipAddressVO = Mockito.mock(IPAddressVO.class);
Mockito.when(ipAddresses.getIp4Address()).thenReturn(ipAddress);
Mockito.when(testOrchastrator._ipAddrMgr.acquireGuestIpAddress(network, ipAddress)).thenReturn(ipAddress);
Ip ip = mock(Ip.class);
Mockito.when(ipAddressVO.getAddress()).thenReturn(ip);
Mockito.when(testOrchastrator._ipAddressDao.findByIp(ipAddress)).thenReturn(ipAddressVO);
IPAddressVO guestIp = testOrchastrator.getGuestIpForNicImport(network, dataCenter, ipAddresses);
Assert.assertEquals(ip, guestIp.getAddress());
}
@Test
public void testGetGuestIpForNicImportBasicZone() {
Network network = Mockito.mock(Network.class);
DataCenter dataCenter = Mockito.mock(DataCenter.class);
Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class);
Mockito.when(network.getGuestType()).thenReturn(GuestType.Isolated);
Mockito.when(dataCenter.getNetworkType()).thenReturn(DataCenter.NetworkType.Basic);
long networkId = 1L;
long dataCenterId = 1L;
IPAddressVO ipAddressVO = Mockito.mock(IPAddressVO.class);
Ip ip = mock(Ip.class);
Mockito.when(ipAddressVO.getAddress()).thenReturn(ip);
Mockito.when(network.getId()).thenReturn(networkId);
Mockito.when(dataCenter.getId()).thenReturn(dataCenterId);
Mockito.when(testOrchastrator._ipAddressDao.findBySourceNetworkIdAndDatacenterIdAndState(networkId, dataCenterId, State.Free)).thenReturn(ipAddressVO);
IPAddressVO guestIp = testOrchastrator.getGuestIpForNicImport(network, dataCenter, ipAddresses);
Assert.assertEquals(ip, guestIp.getAddress());
}
}

View File

@ -103,4 +103,6 @@ public interface IPAddressDao extends GenericDao<IPAddressVO, Long> {
List<IPAddressVO> listByNetworkId(long networkId);
void buildQuarantineSearchCriteria(SearchCriteria<IPAddressVO> sc);
IPAddressVO findBySourceNetworkIdAndDatacenterIdAndState(long sourceNetworkId, long dataCenterId, State state);
}

View File

@ -554,4 +554,13 @@ public class IPAddressDaoImpl extends GenericDaoBase<IPAddressVO, Long> implemen
sc.setParametersIfNotNull("quarantinedPublicIpsIdsNIN", quarantinedIpsIdsAllowedToUser);
}
@Override
public IPAddressVO findBySourceNetworkIdAndDatacenterIdAndState(long sourceNetworkId, long dataCenterId, State state) {
SearchCriteria<IPAddressVO> sc = AllFieldsSearch.create();
sc.setParameters("sourcenetwork", sourceNetworkId);
sc.setParameters("dataCenterId", dataCenterId);
sc.setParameters("state", State.Free);
return findOneBy(sc);
}
}

View File

@ -4581,7 +4581,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
Host host, Host lastHost, VirtualMachine.PowerState powerState) {
if (isImport) {
vm.setDataCenterId(zone.getId());
if (hypervisorType == HypervisorType.VMware) {
if (List.of(HypervisorType.VMware, HypervisorType.KVM).contains(hypervisorType)) {
vm.setHostId(host.getId());
}
if (lastHost != null) {

View File

@ -855,7 +855,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
}
private NicProfile importNic(UnmanagedInstanceTO.Nic nic, VirtualMachine vm, Network network, Network.IpAddresses ipAddresses, int deviceId, boolean isDefaultNic, boolean forced) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
Pair<NicProfile, Integer> result = networkOrchestrationService.importNic(nic.getMacAddress(), deviceId, network, isDefaultNic, vm, ipAddresses, forced);
DataCenterVO dataCenterVO = dataCenterDao.findById(network.getDataCenterId());
Pair<NicProfile, Integer> result = networkOrchestrationService.importNic(nic.getMacAddress(), deviceId, network, isDefaultNic, vm, ipAddresses, dataCenterVO, forced);
if (result == null) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("NIC ID: %s import failed", nic.getNicId()));
}
@ -2371,7 +2372,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
cleanupFailedImportVM(userVm);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage())));
}
networkOrchestrationService.importNic(macAddress,0,network, true, userVm, requestedIpPair, true);
networkOrchestrationService.importNic(macAddress,0,network, true, userVm, requestedIpPair, zone, true);
publishVMUsageUpdateResourceCount(userVm, serviceOffering);
return userVm;
}

View File

@ -24,6 +24,7 @@ import java.util.Map;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.dc.DataCenter;
import com.cloud.network.PublicIpQuarantine;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin;
@ -1030,7 +1031,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches
}
@Override
public Pair<NicProfile, Integer> importNic(String macAddress, int deviceId, Network network, Boolean isDefaultNic, VirtualMachine vm, IpAddresses ipAddresses, boolean forced) {
public Pair<NicProfile, Integer> importNic(String macAddress, int deviceId, Network network, Boolean isDefaultNic, VirtualMachine vm, IpAddresses ipAddresses, DataCenter dataCenter, boolean forced) {
return null;
}

View File

@ -367,7 +367,7 @@ public class UnmanagedVMsManagerImplTest {
NicProfile profile = Mockito.mock(NicProfile.class);
Integer deviceId = 100;
Pair<NicProfile, Integer> pair = new Pair<NicProfile, Integer>(profile, deviceId);
when(networkOrchestrationService.importNic(nullable(String.class), nullable(Integer.class), nullable(Network.class), nullable(Boolean.class), nullable(VirtualMachine.class), nullable(Network.IpAddresses.class), Mockito.anyBoolean())).thenReturn(pair);
when(networkOrchestrationService.importNic(nullable(String.class), nullable(Integer.class), nullable(Network.class), nullable(Boolean.class), nullable(VirtualMachine.class), nullable(Network.IpAddresses.class), nullable(DataCenter.class), Mockito.anyBoolean())).thenReturn(pair);
when(volumeDao.findByInstance(Mockito.anyLong())).thenReturn(volumes);
List<UserVmResponse> userVmResponses = new ArrayList<>();
UserVmResponse userVmResponse = new UserVmResponse();