mirror of https://github.com/apache/cloudstack.git
changes:
1. handle vm hostname change for dns registration 2. generate event for dns name collision 3. remove dns record lifecycle subscriber
This commit is contained in:
parent
356781972e
commit
883dc32abf
|
|
@ -877,6 +877,7 @@ public class EventTypes {
|
|||
public static final String EVENT_DNS_ZONE_DELETE = "DNS.ZONE.DELETE";
|
||||
public static final String EVENT_DNS_RECORD_CREATE = "DNS.RECORD.CREATE";
|
||||
public static final String EVENT_DNS_RECORD_DELETE = "DNS.RECORD.DELETE";
|
||||
public static final String EVENT_DNS_NAME_COLLISION = "DNS.NAME.COLLISION";
|
||||
|
||||
static {
|
||||
|
||||
|
|
|
|||
|
|
@ -1376,6 +1376,7 @@ public class ApiConstants {
|
|||
public static final String INSTANCE_ID = "instanceId";
|
||||
public static final String OLD_STATE = "oldState";
|
||||
public static final String NEW_STATE = "newState";
|
||||
public static final String OLD_HOST_NAME = "oldHostName";
|
||||
|
||||
|
||||
public static final String PARAMETER_DESCRIPTION_ACTIVATION_RULE = "Quota tariff's activation rule. It can receive a JS script that results in either " +
|
||||
|
|
|
|||
|
|
@ -24,11 +24,6 @@ import org.apache.cloudstack.dns.exception.DnsProviderException;
|
|||
import com.cloud.utils.component.Adapter;
|
||||
|
||||
public interface DnsProvider extends Adapter {
|
||||
|
||||
interface Topics {
|
||||
String DNS_RECORD_LIFECYCLE = "dns.record.lifecycle";
|
||||
}
|
||||
|
||||
DnsProviderType getProviderType();
|
||||
|
||||
// Validates connectivity to the server
|
||||
|
|
|
|||
|
|
@ -37,12 +37,9 @@ import org.apache.cloudstack.api.response.DnsZoneNetworkMapResponse;
|
|||
import org.apache.cloudstack.api.response.DnsZoneResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.component.Manager;
|
||||
import com.cloud.utils.component.PluggableService;
|
||||
import com.cloud.vm.Nic;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
|
||||
public interface DnsProviderManager extends Manager, PluggableService {
|
||||
|
||||
|
|
@ -75,8 +72,6 @@ public interface DnsProviderManager extends Manager, PluggableService {
|
|||
|
||||
boolean disassociateZoneFromNetwork(DisassociateDnsZoneFromNetworkCmd cmd);
|
||||
|
||||
void deleteDnsRecordForVM(VirtualMachine instance, Network network, Nic nic);
|
||||
|
||||
void checkDnsServerPermission(Account caller, DnsServer dnsServer);
|
||||
|
||||
void checkDnsZonePermission(Account caller, DnsZone dnsZone);
|
||||
|
|
|
|||
|
|
@ -112,7 +112,8 @@ public interface VirtualMachineManager extends Manager {
|
|||
|
||||
interface Topics {
|
||||
String VM_POWER_STATE = "vm.powerstate";
|
||||
String VM_LIFECYCLE = "vm.lifecycle";
|
||||
String VM_LIFECYCLE_STATE = "vm.lifecycle.state";
|
||||
String VM_ACTION = "vm.action";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,10 +18,12 @@ package com.cloud.vm;
|
|||
|
||||
import static com.cloud.event.EventTypes.EVENT_NIC_CREATE;
|
||||
import static com.cloud.event.EventTypes.EVENT_NIC_DELETE;
|
||||
import static com.cloud.event.EventTypes.EVENT_VM_UPDATE;
|
||||
import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality;
|
||||
import static com.cloud.storage.Volume.IOPS_LIMIT;
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
import static com.cloud.vm.VirtualMachineManager.Topics.VM_LIFECYCLE;
|
||||
import static com.cloud.vm.VirtualMachineManager.Topics.VM_ACTION;
|
||||
import static com.cloud.vm.VirtualMachineManager.Topics.VM_LIFECYCLE_STATE;
|
||||
import static org.apache.cloudstack.api.ApiConstants.MAX_IOPS;
|
||||
import static org.apache.cloudstack.api.ApiConstants.MIN_IOPS;
|
||||
|
||||
|
|
@ -163,6 +165,7 @@ import org.apache.commons.lang3.ObjectUtils;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
|
|
@ -1554,7 +1557,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
event.put(ApiConstants.OLD_STATE, oldState != null ? oldState : State.Unknown);
|
||||
event.put(ApiConstants.NEW_STATE, newState);
|
||||
event.put(ApiConstants.TIME_STAMP, System.currentTimeMillis());
|
||||
messageBus.publish(_name, VM_LIFECYCLE, PublishScope.GLOBAL, event);
|
||||
messageBus.publish(_name, VM_LIFECYCLE_STATE, PublishScope.GLOBAL, event);
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Failed to publish lifecycle event for Instance: {}", instance.getUuid(), ex);
|
||||
}
|
||||
|
|
@ -1576,6 +1579,24 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
}
|
||||
|
||||
private void publishVmHostNameUpdateMessageBus(long instanceId, String oldHostName, String hostName) {
|
||||
if (Strings.isBlank(hostName) || oldHostName.equalsIgnoreCase(hostName)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Map<String, Object> event = new HashMap<>();
|
||||
event.put(ApiConstants.EVENT_ID, UUID.randomUUID().toString());
|
||||
event.put(ApiConstants.INSTANCE_ID, instanceId);
|
||||
event.put(ApiConstants.OLD_HOST_NAME, oldHostName);
|
||||
event.put(ApiConstants.HOST_NAME, hostName);
|
||||
event.put(ApiConstants.EVENT_TYPE, EVENT_VM_UPDATE);
|
||||
event.put(ApiConstants.TIME_STAMP, System.currentTimeMillis());
|
||||
messageBus.publish(_name, VM_ACTION, PublishScope.GLOBAL, event);
|
||||
} catch (Exception ex) {
|
||||
logger.error("Failed to publish Instance action event for ID: {}", instanceId, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set NIC as default if VM has no default NIC
|
||||
* @param vmInstance VM instance to be checked
|
||||
|
|
@ -2964,7 +2985,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VM_UPDATE, eventDescription = "updating Vm")
|
||||
@ActionEvent(eventType = EVENT_VM_UPDATE, eventDescription = "updating Vm")
|
||||
public UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException {
|
||||
validateInputsAndPermissionForUpdateVirtualMachineCommand(cmd);
|
||||
|
||||
|
|
@ -3340,6 +3361,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
}
|
||||
|
||||
if (State.Running == vm.getState()) {
|
||||
publishVmHostNameUpdateMessageBus(vm.getId(), vm.getHostName(), hostName);
|
||||
updateDns(vm, hostName);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,24 +17,23 @@
|
|||
|
||||
package org.apache.cloudstack.dns;
|
||||
|
||||
import static com.cloud.event.EventTypes.EVENT_DNS_RECORD_CREATE;
|
||||
import static com.cloud.event.EventTypes.EVENT_DNS_RECORD_DELETE;
|
||||
import static com.cloud.event.EventTypes.EVENT_NIC_CREATE;
|
||||
import static com.cloud.event.EventTypes.EVENT_NIC_DELETE;
|
||||
import static com.cloud.event.EventTypes.EVENT_VM_UPDATE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd;
|
||||
import org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd;
|
||||
|
|
@ -74,21 +73,21 @@ import org.apache.cloudstack.dns.vo.DnsZoneNetworkMapVO;
|
|||
import org.apache.cloudstack.dns.vo.DnsZoneVO;
|
||||
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
||||
import org.apache.cloudstack.framework.messagebus.MessageSubscriber;
|
||||
import org.apache.cloudstack.framework.messagebus.PublishScope;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.domain.dao.DomainDao;
|
||||
import com.cloud.event.ActionEvent;
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.StringUtils;
|
||||
|
|
@ -101,7 +100,6 @@ import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
|
|||
import com.cloud.utils.db.TransactionStatus;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.Nic;
|
||||
import com.cloud.vm.NicDetailVO;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VirtualMachineManager;
|
||||
import com.cloud.vm.dao.NicDao;
|
||||
|
|
@ -460,13 +458,20 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
}
|
||||
try {
|
||||
DnsRecord.RecordType type = cmd.getType();
|
||||
DnsProvider provider = getProviderByType(server.getProviderType());
|
||||
if (provider.dnsRecordExists(server, dnsZone, recordName, type.name())) {
|
||||
throw new CloudRuntimeException(String.format(
|
||||
"DNS record '%s' of type '%s' already exists in DNS zone '%s' (provider: %s). Overwriting is not permitted.",
|
||||
recordName, type.name(), dnsZone.getName(), provider.getName()
|
||||
));
|
||||
}
|
||||
|
||||
List<String> normalizedContents = cmd.getContents().stream()
|
||||
.map(value -> DnsProviderUtil.normalizeDnsRecordValue(value, type)).collect(Collectors.toList());
|
||||
DnsRecord record = new DnsRecord(recordName, type, normalizedContents, cmd.getTtl());
|
||||
DnsProvider provider = getProviderByType(server.getProviderType());
|
||||
|
||||
String normalizedRecordName = provider.addRecord(server, dnsZone, record);
|
||||
record.setName(normalizedRecordName);
|
||||
publishDnsRecordEventMessageBus(recordName, type, caller.getAccountId(), EVENT_DNS_RECORD_CREATE, normalizedContents);
|
||||
return createDnsRecordResponse(record);
|
||||
} catch (Exception ex) {
|
||||
logger.error("Failed to add DNS record via provider", ex);
|
||||
|
|
@ -477,21 +482,20 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_DNS_RECORD_DELETE, eventDescription = "Deleting DNS Record")
|
||||
public boolean deleteDnsRecord(DeleteDnsRecordCmd cmd) {
|
||||
DnsZoneVO zone = dnsZoneDao.findById(cmd.getDnsZoneId());
|
||||
if (zone == null) {
|
||||
DnsZoneVO dnsZone = dnsZoneDao.findById(cmd.getDnsZoneId());
|
||||
if (dnsZone == null) {
|
||||
throw new InvalidParameterValueException("DNS zone not found.");
|
||||
}
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
accountMgr.checkAccess(caller, null, true, zone);
|
||||
DnsServerVO server = dnsServerDao.findById(zone.getDnsServerId());
|
||||
accountMgr.checkAccess(caller, null, true, dnsZone);
|
||||
DnsServerVO server = dnsServerDao.findById(dnsZone.getDnsServerId());
|
||||
DnsRecord.RecordType recordType = cmd.getType();
|
||||
try {
|
||||
DnsRecord record = new DnsRecord();
|
||||
record.setName(cmd.getName());
|
||||
record.setType(recordType);
|
||||
DnsProvider provider = getProviderByType(server.getProviderType());
|
||||
String deletedDnsRecord = provider.deleteRecord(server, zone, record);
|
||||
publishDnsRecordEventMessageBus(deletedDnsRecord, recordType, caller.getAccountId(), EVENT_DNS_RECORD_DELETE, null);
|
||||
String deletedDnsRecord = provider.deleteRecord(server, dnsZone, record);
|
||||
return deletedDnsRecord != null;
|
||||
} catch (Exception ex) {
|
||||
logger.error("Failed to delete DNS record via provider", ex);
|
||||
|
|
@ -705,77 +709,6 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
return dnsZoneNetworkMapDao.remove(mapping.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDnsRecordForVM(VirtualMachine instance, Network network, Nic nic) {
|
||||
String instanceName = instance.getInstanceName();
|
||||
NicDetailVO nicDetailVO = nicDetailsDao.findDetail(nic.getId(), ApiConstants.NIC_DNS_RECORD);
|
||||
if (nicDetailVO == null || Strings.isBlank(nicDetailVO.getValue())) {
|
||||
logger.debug("No DNS record found for Instance: {}", instance.getInstanceName());
|
||||
return;
|
||||
}
|
||||
String dnsRecord = nicDetailVO.getValue();
|
||||
try {
|
||||
DnsZoneNetworkMapVO dnsZoneNetworkMap = dnsZoneNetworkMapDao.findByNetworkId(network.getId());
|
||||
DnsZoneVO dnsZone = null;
|
||||
DnsServerVO dnsServer = null;
|
||||
if (dnsZoneNetworkMap != null) {
|
||||
dnsZone = dnsZoneDao.findById(dnsZoneNetworkMap.getDnsZoneId());
|
||||
}
|
||||
if (dnsZone != null) {
|
||||
dnsServer = dnsServerDao.findById(dnsZone.getDnsServerId());
|
||||
}
|
||||
if (dnsServer != null) {
|
||||
processDnsRecordInProvider(dnsRecord, instance, dnsServer, dnsZone, nic, false);
|
||||
} else {
|
||||
logger.warn("Skipping deletion of DNS record: {} from provider for Instance: {}.", dnsRecord, instanceName);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.error("Failed deleting DNS record: {} for Instance: {}, proceeding with DB cleanup.", dnsRecord, instanceName);
|
||||
} finally {
|
||||
nicDetailsDao.removeDetail(nic.getId(), ApiConstants.NIC_DNS_RECORD);
|
||||
logger.debug("Removed DNS record from DB for Instance: {}, NIC ID: {}", instanceName, nic.getUuid());
|
||||
}
|
||||
}
|
||||
|
||||
private String processDnsRecordInProvider(String recordName, VirtualMachine instance, DnsServer server, DnsZone dnsZone,
|
||||
Nic nic, boolean isAdd) {
|
||||
|
||||
try {
|
||||
DnsProvider provider = getProviderByType(server.getProviderType());
|
||||
// Handle IPv4 (A Record)
|
||||
String ipv4DnsRecord = null;
|
||||
if (nic.getIPv4Address() != null) {
|
||||
DnsRecord recordA = new DnsRecord(recordName, DnsRecord.RecordType.A, Collections.singletonList(nic.getIPv4Address()), 3600);
|
||||
if (isAdd) {
|
||||
ipv4DnsRecord = provider.addRecord(server, dnsZone, recordA);
|
||||
} else {
|
||||
ipv4DnsRecord = provider.deleteRecord(server, dnsZone, recordA);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle IPv6 (AAAA Record) if it exists
|
||||
String ipv6DnsRecord = null;
|
||||
if (nic.getIPv6Address() != null) {
|
||||
DnsRecord recordAAAA = new DnsRecord(recordName, DnsRecord.RecordType.AAAA, Collections.singletonList(nic.getIPv6Address()), 3600);
|
||||
if (isAdd) {
|
||||
ipv6DnsRecord = provider.addRecord(server, dnsZone, recordAAAA);
|
||||
} else {
|
||||
ipv6DnsRecord = provider.deleteRecord(server, dnsZone, recordAAAA);
|
||||
}
|
||||
}
|
||||
return ipv4DnsRecord != null ? ipv4DnsRecord : ipv6DnsRecord;
|
||||
} catch (Exception ex) {
|
||||
logger.error(
|
||||
"Failed to {} DNS record for Instance {} in zone {}",
|
||||
isAdd ? "register" : "remove",
|
||||
instance.getInstanceName(),
|
||||
dnsZone.getName(),
|
||||
ex
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkDnsServerPermission(Account caller, DnsServer dnsServer) throws PermissionDeniedException {
|
||||
if (caller.getId() == dnsServer.getAccountId()) {
|
||||
|
|
@ -813,9 +746,9 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
|
||||
@Override
|
||||
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||
messageBus.subscribe(VirtualMachineManager.Topics.VM_LIFECYCLE, new VmLifecycleSubscriber());
|
||||
messageBus.subscribe(VirtualMachineManager.Topics.VM_LIFECYCLE_STATE, new VmLifecycleSubscriber());
|
||||
messageBus.subscribe(Nic.Topics.NIC_LIFECYCLE, new NicLifecycleSubscriber());
|
||||
messageBus.subscribe(DnsProvider.Topics.DNS_RECORD_LIFECYCLE, new DnsRecordLifecycleSubscriber());
|
||||
messageBus.subscribe(VirtualMachineManager.Topics.VM_ACTION, new VmRenameActionSubscriber());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -916,57 +849,33 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
}
|
||||
}
|
||||
|
||||
class DnsRecordLifecycleSubscriber implements MessageSubscriber {
|
||||
class VmRenameActionSubscriber implements MessageSubscriber {
|
||||
|
||||
@Override
|
||||
public void onPublishMessage(String senderAddress, String subject, Object args) {
|
||||
try {
|
||||
logger.trace("DNS record lifecycle event: {}, {}, {}", senderAddress, subject, args);
|
||||
logger.trace("VM action event: {}, {}, {}", senderAddress, subject, args);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> event = (Map<String, Object>) args;
|
||||
String eventType = (String) event.get(ApiConstants.EVENT_TYPE);
|
||||
String dnsRecord = (String) event.get(ApiConstants.DNS_RECORD);
|
||||
if (EVENT_DNS_RECORD_CREATE.equalsIgnoreCase(eventType)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> contents = (List<String>) event.get(ApiConstants.CONTENTS);
|
||||
if (CollectionUtils.isNotEmpty(contents)) {
|
||||
for (String ipAddress : contents) {
|
||||
Nic nic = nicDao.findByIpAddressAndVmType(ipAddress, VirtualMachine.Type.User);
|
||||
if (nic != null) {
|
||||
nicDetailsDao.addDetail(nic.getId(), ApiConstants.NIC_DNS_RECORD, dnsRecord, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (EVENT_DNS_RECORD_DELETE.equalsIgnoreCase(eventType)) {
|
||||
nicDetailsDao.removeDetailsForValuesIn(ApiConstants.NIC_DNS_RECORD, Collections.singletonList(dnsRecord));
|
||||
if (!eventType.equalsIgnoreCase(EVENT_VM_UPDATE)) {
|
||||
return;
|
||||
}
|
||||
String newHostName = (String) event.get(ApiConstants.HOST_NAME);
|
||||
String oldHostName = (String) event.get(ApiConstants.OLD_HOST_NAME);
|
||||
if (oldHostName.equalsIgnoreCase(newHostName)) {
|
||||
logger.debug("Instance hostname is unchanged, skip event processing");
|
||||
return;
|
||||
}
|
||||
long instanceId = (long) event.get(ApiConstants.INSTANCE_ID);
|
||||
handleVmHostnameChanged(instanceId, newHostName);
|
||||
} catch (Exception ex) {
|
||||
logger.error("Failed to process DNS record lifecycle event", ex);
|
||||
logger.error("Failed to process Instance action event", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void publishDnsRecordEventMessageBus(String dnsRecord, DnsRecord.RecordType recordType, Long accountId,
|
||||
String eventType, List<String> contents) {
|
||||
|
||||
// Only publish for A or AAAA records and non-null record name
|
||||
if ((recordType != DnsRecord.RecordType.A && recordType != DnsRecord.RecordType.AAAA) || dnsRecord == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Map<String, Object> event = new HashMap<>();
|
||||
event.put(ApiConstants.EVENT_ID, UUID.randomUUID().toString());
|
||||
event.put(ApiConstants.DNS_RECORD, dnsRecord);
|
||||
event.put(ApiConstants.ACCOUNT_ID, accountId);
|
||||
event.put(ApiConstants.EVENT_TYPE, eventType);
|
||||
event.put(ApiConstants.CONTENTS, contents != null ? contents : Collections.emptyList());
|
||||
event.put(ApiConstants.TIME_STAMP, System.currentTimeMillis());
|
||||
messageBus.publish(_name, DnsProvider.Topics.DNS_RECORD_LIFECYCLE, PublishScope.GLOBAL, event);
|
||||
} catch (Exception ex) {
|
||||
logger.error("Failed to publish {} event for DNS record: {}", eventType, dnsRecord, ex);
|
||||
}
|
||||
}
|
||||
|
||||
void handleVmRunningState(long instanceId) {
|
||||
VirtualMachine instance = vmInstanceDao.findById(instanceId);
|
||||
if (instance == null) {
|
||||
|
|
@ -999,16 +908,13 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
Transaction.execute(new TransactionCallbackWithExceptionNoReturn<DnsProviderException>() {
|
||||
@Override
|
||||
public void doInTransactionWithoutResult(TransactionStatus status) throws DnsProviderException{
|
||||
DnsNicJoinVO existing = dnsNicJoinDao.findActiveByDnsRecordAndZone(dnsRecordUrl, targetZoneId);
|
||||
if (existing != null && existing.getInstanceId() != instanceId) {
|
||||
logger.error("DNS Collision: Cannot register DNS record: {}. Already owned by Instance: {}.",
|
||||
dnsRecordUrl, existing.getInstanceId());
|
||||
if (isDnsCollision(dnsRecordUrl, targetZoneId, instanceId)) {
|
||||
return;
|
||||
}
|
||||
for (DnsNicJoinVO nic : nicsForThisFqdn) {
|
||||
nicDetailsDao.addDetail(nic.getId(), ApiConstants.NIC_DNS_RECORD, dnsRecordUrl, true);
|
||||
}
|
||||
syncDnsState(instanceId, dnsRecordUrl, targetZoneId);
|
||||
syncDnsRecordsState(instanceId, dnsRecordUrl, targetZoneId);
|
||||
}
|
||||
});
|
||||
} catch (DnsProviderException ex) {
|
||||
|
|
@ -1052,7 +958,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
}
|
||||
// Because we just deleted the nic_details, the sync method will naturally
|
||||
// find 0 active IPs for this VM/FQDN combo and issue a clean DELETE to PowerDNS.
|
||||
syncDnsState(instanceId, dnsRecordUrl, targetZoneId);
|
||||
syncDnsRecordsState(instanceId, dnsRecordUrl, targetZoneId);
|
||||
}
|
||||
});
|
||||
logger.debug("Successfully cleaned up DNS record: {} for Instance with ID: {}", dnsRecordUrl, instanceId);
|
||||
|
|
@ -1065,6 +971,78 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
}
|
||||
}
|
||||
|
||||
void handleVmHostnameChanged(long instanceId, String newHostName) {
|
||||
VirtualMachine instance = vmInstanceDao.findById(instanceId);
|
||||
if (instance == null) {
|
||||
logger.debug("Instance is not found for the given ID: {}", instanceId);
|
||||
return;
|
||||
}
|
||||
|
||||
List<DnsNicJoinVO> mappedNics = dnsNicJoinDao.listActiveByVmId(instanceId);
|
||||
if (CollectionUtils.isEmpty(mappedNics)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Long, Map<String, List<DnsNicJoinVO>>> dnsZoneNewRecordNicMap = new HashMap<>();
|
||||
for (DnsNicJoinVO nic : mappedNics) {
|
||||
DnsZoneVO targetZone = dnsZoneDao.findById(nic.getDnsZoneId());
|
||||
if (targetZone == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String oldDnsRecordUrl = nic.getNicDnsUrl();
|
||||
String newDnsRecordUrl = prepareDnsRecordUrl(newHostName, nic.getSubDomain(), targetZone.getName());
|
||||
if (newDnsRecordUrl.equals(oldDnsRecordUrl)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dnsZoneNewRecordNicMap.computeIfAbsent(targetZone.getId(), k -> new HashMap<>())
|
||||
.computeIfAbsent(newDnsRecordUrl, k -> new ArrayList<>())
|
||||
.add(nic);
|
||||
}
|
||||
|
||||
for (Map.Entry<Long, Map<String, List<DnsNicJoinVO>>> zoneEntry : dnsZoneNewRecordNicMap.entrySet()) {
|
||||
long targetZoneId = zoneEntry.getKey();
|
||||
|
||||
for (Map.Entry<String, List<DnsNicJoinVO>> newUrlEntry : zoneEntry.getValue().entrySet()) {
|
||||
String newDnsRecordUrl = newUrlEntry.getKey();
|
||||
List<DnsNicJoinVO> nicsForThisFqdn = newUrlEntry.getValue();
|
||||
|
||||
try {
|
||||
Transaction.execute(new TransactionCallbackWithExceptionNoReturn<DnsProviderException>() {
|
||||
@Override
|
||||
public void doInTransactionWithoutResult(TransactionStatus status) throws DnsProviderException {
|
||||
if (isDnsCollision(newDnsRecordUrl, targetZoneId, instanceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> oldDnsRecordUrls = new HashSet<>();
|
||||
for (DnsNicJoinVO nic : nicsForThisFqdn) {
|
||||
if (nic.getNicDnsUrl() != null) {
|
||||
oldDnsRecordUrls.add(nic.getNicDnsUrl());
|
||||
}
|
||||
nicDetailsDao.addDetail(nic.getId(), ApiConstants.NIC_DNS_RECORD, newDnsRecordUrl, true);
|
||||
}
|
||||
|
||||
// NICs for the old URL and cleanly send a DELETE API call to PowerDNS!
|
||||
for (String oldUrl : oldDnsRecordUrls) {
|
||||
syncDnsRecordsState(instanceId, oldUrl, targetZoneId);
|
||||
}
|
||||
|
||||
// This sync call finds the newly written intent and sends an ADD/REPLACE call.
|
||||
syncDnsRecordsState(instanceId, newDnsRecordUrl, targetZoneId);
|
||||
}
|
||||
});
|
||||
logger.debug("Successfully handled DNS Rename to: {}", newDnsRecordUrl);
|
||||
|
||||
} catch (Exception ex) {
|
||||
logger.error("Failed to process VM Rename for Instance: {}", instance.getUuid(), ex);
|
||||
throw new CloudRuntimeException(String.format("DNS API Sync Failed for Rename to %s", newDnsRecordUrl), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleNicPlug(long instanceId, long nicId) {
|
||||
VirtualMachine instance = vmInstanceDao.findById(instanceId);
|
||||
if (instance == null || instance.getState() != VirtualMachine.State.Running) {
|
||||
|
|
@ -1087,14 +1065,11 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
|
||||
@Override
|
||||
public void doInTransactionWithoutResult(TransactionStatus status) throws DnsProviderException {
|
||||
DnsNicJoinVO existing = dnsNicJoinDao.findActiveByDnsRecordAndZone(dnsRecordUrl, targetZone.getId());
|
||||
if (existing != null && existing.getInstanceId() != instanceId) {
|
||||
logger.error("DNS Collision: Cannot register DNS record: {}. Already owned by Instance: {}.",
|
||||
dnsRecordUrl, existing.getInstanceId());
|
||||
if (isDnsCollision(dnsRecordUrl, targetZone.getId(), instanceId)) {
|
||||
return;
|
||||
}
|
||||
nicDetailsDao.addDetail(nicId, ApiConstants.NIC_DNS_RECORD, dnsRecordUrl, true);
|
||||
syncDnsState(instanceId, dnsRecordUrl, targetZone.getId());
|
||||
syncDnsRecordsState(instanceId, dnsRecordUrl, targetZone.getId());
|
||||
logger.debug("Successfully synced DNS on NIC Plug for: {}", dnsRecordUrl);
|
||||
}
|
||||
});
|
||||
|
|
@ -1118,7 +1093,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
@Override
|
||||
public void doInTransactionWithoutResult(TransactionStatus status) throws DnsProviderException {
|
||||
nicDetailsDao.removeDetail(nicId, ApiConstants.NIC_DNS_RECORD);
|
||||
syncDnsState(instanceId, dnsRecordUrl, dnsZoneId);
|
||||
syncDnsRecordsState(instanceId, dnsRecordUrl, dnsZoneId);
|
||||
logger.debug("Successfully synced DNS record: {} on NIC unplug", dnsRecordUrl);
|
||||
}
|
||||
});
|
||||
|
|
@ -1139,7 +1114,22 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
|
|||
return String.join(".", parts);
|
||||
}
|
||||
|
||||
public void syncDnsState(Long instanceId, String dnsRecordUrl, long dnsZoneId) throws DnsProviderException {
|
||||
private boolean isDnsCollision(String dnsRecordUrl, long targetZoneId, long instanceId) {
|
||||
DnsNicJoinVO existing = dnsNicJoinDao.findActiveByDnsRecordAndZone(dnsRecordUrl, targetZoneId);
|
||||
if (existing != null && existing.getInstanceId() != instanceId) {
|
||||
logger.error("DNS collision: cannot register DNS record: {}. Already owned by Instance: {}.",
|
||||
dnsRecordUrl, existing.getInstanceId());
|
||||
|
||||
String description = String.format("Instance hostname change resulted in a DNS collision. " +
|
||||
"The requested DNS record '%s' is already in use.", dnsRecordUrl);
|
||||
ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, Domain.ROOT_DOMAIN,
|
||||
EventTypes.EVENT_DNS_NAME_COLLISION, description, instanceId, ApiCommandResourceType.VirtualMachine.toString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void syncDnsRecordsState(Long instanceId, String dnsRecordUrl, long dnsZoneId) throws DnsProviderException {
|
||||
DnsZone dnsZone = dnsZoneDao.findById(dnsZoneId);
|
||||
if (dnsZone == null) {
|
||||
logger.error("DNS zone not found for the provided ID: {}", dnsZoneId);
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ import org.springframework.test.util.ReflectionTestUtils;
|
|||
import com.cloud.domain.dao.DomainDao;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.user.Account;
|
||||
|
|
@ -90,9 +89,6 @@ import com.cloud.user.AccountVO;
|
|||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallback;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.NicDetailVO;
|
||||
import com.cloud.vm.NicVO;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.dao.NicDao;
|
||||
import com.cloud.vm.dao.NicDetailsDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
|
|
@ -573,32 +569,6 @@ public class DnsProviderManagerImplTest {
|
|||
manager.checkDnsZonePermission(callerMock, zoneVO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteDnsRecordForVMNoNicDetail() {
|
||||
Network network = mock(Network.class);
|
||||
NicVO nic = mock(NicVO.class);
|
||||
VMInstanceVO vm = mock(VMInstanceVO.class);
|
||||
when(nic.getId()).thenReturn(50L);
|
||||
when(vm.getInstanceName()).thenReturn("vm-1");
|
||||
when(nicDetailsDao.findDetail(50L, "nicdnsrecord")).thenReturn(null);
|
||||
manager.deleteDnsRecordForVM(vm, network, nic);
|
||||
verify(dnsZoneNetworkMapDao, never()).findByNetworkId(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteDnsRecordForVMNicDetailBlankValue() {
|
||||
Network network = mock(Network.class);
|
||||
NicVO nic = mock(NicVO.class);
|
||||
VMInstanceVO vm = mock(VMInstanceVO.class);
|
||||
NicDetailVO detail = mock(NicDetailVO.class);
|
||||
when(nic.getId()).thenReturn(50L);
|
||||
when(vm.getInstanceName()).thenReturn("vm-1");
|
||||
when(nicDetailsDao.findDetail(50L, "nicdnsrecord")).thenReturn(detail);
|
||||
when(detail.getValue()).thenReturn(" ");
|
||||
manager.deleteDnsRecordForVM(vm, network, nic);
|
||||
verify(dnsZoneNetworkMapDao, never()).findByNetworkId(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCommandsReturnsNonEmptyList() {
|
||||
List<Class<?>> commands = manager.getCommands();
|
||||
|
|
@ -720,31 +690,6 @@ public class DnsProviderManagerImplTest {
|
|||
verify(dnsProviderMock).deleteRecord(any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteDnsRecordForVMSuccess() throws Exception {
|
||||
Network network = mock(Network.class);
|
||||
NicVO nic = mock(NicVO.class);
|
||||
when(nic.getIPv4Address()).thenReturn("1.2.3.4");
|
||||
VMInstanceVO vm = mock(VMInstanceVO.class);
|
||||
NicDetailVO detail = mock(NicDetailVO.class);
|
||||
when(nic.getId()).thenReturn(50L);
|
||||
when(vm.getInstanceName()).thenReturn("vm-1");
|
||||
when(nicDetailsDao.findDetail(50L, "nicdnsrecord")).thenReturn(detail);
|
||||
when(detail.getValue()).thenReturn("vm-1.ex.com");
|
||||
|
||||
DnsZoneNetworkMapVO mapping = mock(DnsZoneNetworkMapVO.class);
|
||||
when(network.getId()).thenReturn(NETWORK_ID);
|
||||
when(dnsZoneNetworkMapDao.findByNetworkId(NETWORK_ID)).thenReturn(mapping);
|
||||
when(mapping.getDnsZoneId()).thenReturn(ZONE_ID);
|
||||
when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO);
|
||||
when(dnsServerDao.findById(anyLong())).thenReturn(serverVO);
|
||||
when(dnsProviderMock.deleteRecord(any(), any(), any())).thenReturn("vm-1.ex.com");
|
||||
|
||||
manager.deleteDnsRecordForVM(vm, network, nic);
|
||||
verify(dnsProviderMock).deleteRecord(any(), any(), any());
|
||||
verify(nicDetailsDao).removeDetail(50L, "nicdnsrecord");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigure() throws Exception {
|
||||
assertTrue(manager.configure("dnsProviderManagerImpl", Collections.emptyMap()));
|
||||
|
|
|
|||
Loading…
Reference in New Issue