From 855f611bfad35b05c6e7bd553edf78b2d411a132 Mon Sep 17 00:00:00 2001 From: Sachin R <32716246+sachindoddaguni@users.noreply.github.com> Date: Thu, 4 Dec 2025 14:42:35 -0800 Subject: [PATCH] * Use hostname during MS registration to avoid duplicate registrations --- .../com/cloud/cluster/ClusterManagerImpl.java | 31 +++++++- .../cluster/dao/ManagementServerHostDao.java | 2 + .../dao/ManagementServerHostDaoImpl.java | 31 ++++++++ .../dao/ManagementServerHostDaoImplTest.java | 72 +++++++++++++++++++ 4 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 framework/cluster/src/test/java/com/cloud/cluster/dao/ManagementServerHostDaoImplTest.java diff --git a/framework/cluster/src/main/java/com/cloud/cluster/ClusterManagerImpl.java b/framework/cluster/src/main/java/com/cloud/cluster/ClusterManagerImpl.java index 2a282cbb1ef..997fe6de48a 100644 --- a/framework/cluster/src/main/java/com/cloud/cluster/ClusterManagerImpl.java +++ b/framework/cluster/src/main/java/com/cloud/cluster/ClusterManagerImpl.java @@ -40,6 +40,7 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.commons.lang3.StringUtils; import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; @@ -1043,13 +1044,39 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C final Class c = this.getClass(); final String version = c.getPackage().getImplementationVersion(); + final String currentHostname = NetUtils.getCanonicalHostName(); ManagementServerHostVO mshost = _mshostDao.findByMsid(_msId); + + // Look for duplicate hostname in the case of kubernetes setup where ip/mac changes but hostname is constant. + if (mshost == null && StringUtils.isNotBlank(currentHostname)) { + ManagementServerHostVO activeEntry = _mshostDao.findByName(currentHostname); + if (activeEntry != null) { + // Found an active entry with this hostname but different MSID + // This happens when a pod restarts with a new MAC address (new MSID) + if (activeEntry.getMsid() != _msId) { + logger.info(String.format( + "Found active entry for hostname '%s' with old MSID %d. " + + "Marking it as removed and creating new entry with MSID %d.", + currentHostname, activeEntry.getMsid(), _msId)); + // Mark the old entry as removed + activeEntry.setRemoved(DateUtil.currentGMTTime()); + activeEntry.setState(ManagementServerHost.State.Down); + _mshostDao.update(activeEntry.getId(), activeEntry); + // Set mshost to null so a new entry will be created below + mshost = null; + } else { + // Same MSID - this is our existing entry, use the update path + mshost = activeEntry; + } + } + } + if (mshost == null) { mshost = new ManagementServerHostVO(); mshost.setMsid(_msId); mshost.setRunid(_runId); - mshost.setName(NetUtils.getCanonicalHostName()); + mshost.setName(currentHostname); mshost.setVersion(version); mshost.setServiceIP(_clusterNodeIP); mshost.setServicePort(_currentServiceAdapter.getServicePort()); @@ -1063,7 +1090,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C logger.info("New instance of management server {}, runId {} is being started", mshost, _runId); } } else { - _mshostDao.update(mshost.getId(), _runId, NetUtils.getCanonicalHostName(), version, _clusterNodeIP, _currentServiceAdapter.getServicePort(), + _mshostDao.update(mshost.getId(), _runId, currentHostname, version, _clusterNodeIP, _currentServiceAdapter.getServicePort(), DateUtil.currentGMTTime()); if (logger.isInfoEnabled()) { logger.info("Management server {}, runId {} is being started", mshost, _runId); diff --git a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDao.java b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDao.java index 96d57ee0425..adcabc93028 100644 --- a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDao.java +++ b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDao.java @@ -31,6 +31,8 @@ public interface ManagementServerHostDao extends GenericDao implements ManagementServerHostDao { private final SearchBuilder MsIdSearch; + private final SearchBuilder NameSearch; private final SearchBuilder ActiveSearch; private final SearchBuilder InactiveSearch; private final SearchBuilder StateSearch; @@ -75,6 +76,32 @@ public class ManagementServerHostDaoImpl extends GenericDaoBase sc = NameSearch.create(); + sc.setParameters("name", name); + // Only search for active (non-removed) entries to avoid non-deterministic results + // when multiple removed entries exist for the same hostname + sc.addAnd("removed", SearchCriteria.Op.NULL); + + List l = listBy(sc); + if (l != null && l.size() > 0) { + if (l.size() > 1) { + s_logger.error(String.format( + "Data corruption detected: Found %d active entries for hostname '%s'. " + + "This should never happen and indicates duplicate mshost records.", + l.size(), name)); + } + return l.get(0); + } + + return null; + } + @Override @DB public void update(long id, long runid, String name, String version, String serviceIP, int servicePort, Date lastUpdate) { @@ -191,6 +218,10 @@ public class ManagementServerHostDaoImpl extends GenericDaoBase