From 70fe6027caa72615b340265dcfdea0515e59bce0 Mon Sep 17 00:00:00 2001 From: DamonB Date: Fri, 7 Aug 2026 13:02:51 -0500 Subject: [PATCH 1/3] Ignore .envrc --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index abaef83e4555..6eaa2f0bcbfa 100644 --- a/.gitignore +++ b/.gitignore @@ -99,6 +99,7 @@ tools/marvin/marvin/cloudstackAPI/* unittest venv waf-* +.envrc # this ignores _all files starting with '.'. Don't do that! #.* From fc203b6cadfd11af29c3c4db4150f082784c7306 Mon Sep 17 00:00:00 2001 From: DamonB Date: Fri, 7 Aug 2026 13:40:17 -0500 Subject: [PATCH 2/3] Handle msid ennsure stable identity, and msid entegrity --- .../jobs/impl/AsyncJobManagerImpl.java | 2 +- .../utils/identity/ManagementServerNode.java | 125 ++++++++++++------ 2 files changed, 86 insertions(+), 41 deletions(-) diff --git a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java index 7672b9dc6f97..87b3f84f30d0 100644 --- a/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java +++ b/framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java @@ -1153,7 +1153,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) { final List jobs = _jobDao.getResetJobs(msid); for (final AsyncJobVO job : jobs) { if (logger.isDebugEnabled()) { - logger.debug("Cancel left-over job-" + job.getId()); + logger.debug("Cancel left-over job-{} for msid {}", job.getId(), msid); } cleanupResources(job); job.setStatus(JobInfo.Status.FAILED); diff --git a/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java b/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java index d48f300ab59e..43cf3b92f4ba 100644 --- a/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java +++ b/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java @@ -46,64 +46,109 @@ public class ManagementServerNode extends AdapterBase implements SystemIntegrity private static final String FQDN_ENV_VAR = "CLOUDSTACK_MSID_FROM_FQDN"; private static final String FQDN_SYS_PROP = "cloudstack.msid.from.fqdn"; + private static final String IDENTITY_ENV_VAR = "CLOUDSTACK_MSID_IDENTITY"; + private static final String IDENTITY_SYS_PROP = "cloudstack.msid.identity"; + private static final String HOSTNAME_ENV_VAR = "HOSTNAME"; + private static final String POD_NAMESPACE_ENV_VAR = "POD_NAMESPACE"; + + // op_lock.mac is varchar(17) and holds the msid, so the id must stay within the 48-bit MAC address range. + private static final int MSID_BYTES = 6; private static String s_nodeIdSource; - private static Exception s_initError; private static final long s_nodeId = initNodeId(); private static long initNodeId() { - if (isFqdnModeEnabled()) { - return generateIdFromFqdn(); + if (isTruthy(System.getenv(FQDN_ENV_VAR)) || isTruthy(System.getProperty(FQDN_SYS_PROP))) { + return generateIdFromStableIdentity(); } + s_nodeIdSource = "mac-address"; return MacAddress.getMacAddress().toLong(); } - private static boolean isFqdnModeEnabled() { - return isTruthy(System.getenv(FQDN_ENV_VAR)) || isTruthy(System.getProperty(FQDN_SYS_PROP)); - } + static String resolveNodeIdentity(String explicitIdentity, String hostnameEnv, String podNamespaceEnv, + String detectedHostName, String canonicalHostName) { + String configuredIdentity = trimToNull(explicitIdentity); + if (configuredIdentity != null) { + return configuredIdentity; + } - private static boolean isTruthy(String value) { - if (value == null) { - return false; + String hostName = trimToNull(hostnameEnv); + if (hostName != null) { + String podNamespace = trimToNull(podNamespaceEnv); + return podNamespace == null ? hostName : hostName + "." + podNamespace; } - String trimmed = value.trim(); - return "true".equalsIgnoreCase(trimmed) || "1".equals(trimmed) || "yes".equalsIgnoreCase(trimmed); + + String detected = trimToNull(detectedHostName); + String canonical = trimToNull(canonicalHostName); + if (detected != null && canonical != null && !detected.equals(canonical)) { + return detected + "|" + canonical; + } + + return canonical != null ? canonical : detected; } - /** - * Derives a stable node id from a SHA-256 hash of the local FQDN. - * - *

On failure it records the cause and returns {@code 0} (an invalid id) rather than - * silently reverting to an unstable MAC-based id. The invalid id makes {@link #check()} - * fail the system-integrity check, which stops startup cleanly via {@link #start()} - * instead of raising an {@code ExceptionInInitializerError} from static initialization. - * - * @return a positive, non-zero 48-bit id, or {@code 0} if it cannot be derived - */ - private static long generateIdFromFqdn() { + static long hashNodeIdentity(String nodeIdentity) { try { - String fqdn = InetAddress.getLocalHost().getCanonicalHostName(); - s_nodeIdSource = "fqdn:" + fqdn; MessageDigest digest = MessageDigest.getInstance("SHA-256"); - byte[] hash = digest.digest(fqdn.getBytes(StandardCharsets.UTF_8)); + byte[] hash = digest.digest(nodeIdentity.getBytes(StandardCharsets.UTF_8)); long id = 0; - for (int i = 0; i < 6; i++) { - id = (id << 8) | (hash[i] & 0xFF); + for (int i = 0; i < MSID_BYTES; i++) { + id = (id << 8) | (hash[i] & 0xFFL); } - // Ensure positive and non-zero - id = id & 0x7FFFFFFFFFFFFFFFL; - if (id == 0) { - id = 1; + + return id == 0 ? 1 : id; + } catch (NoSuchAlgorithmException e) { + throw new CloudRuntimeException("SHA-256 algorithm not available for management server ID generation", e); + } + } + + private static long generateIdFromStableIdentity() { + try { + InetAddress localHost = InetAddress.getLocalHost(); + String nodeIdentity = resolveNodeIdentity( + firstNonBlank(System.getProperty(IDENTITY_SYS_PROP), System.getenv(IDENTITY_ENV_VAR)), + System.getenv(HOSTNAME_ENV_VAR), + System.getenv(POD_NAMESPACE_ENV_VAR), + localHost.getHostName(), + localHost.getCanonicalHostName()); + + if (nodeIdentity == null) { + throw new CloudRuntimeException("Unable to resolve a stable management server identity"); } - return id; + + s_nodeIdSource = "identity:" + nodeIdentity; + return hashNodeIdentity(nodeIdentity); + } catch (CloudRuntimeException e) { + throw e; } catch (Exception e) { - s_nodeIdSource = "fqdn-error"; - s_initError = e; - return 0; + throw new CloudRuntimeException("Unable to generate management server ID from host identity", e); } } + private static String firstNonBlank(String first, String second) { + String value = trimToNull(first); + return value != null ? value : trimToNull(second); + } + + private static boolean isTruthy(String value) { + if (value == null) { + return false; + } + + String trimmed = value.trim(); + return "true".equalsIgnoreCase(trimmed) || "1".equals(trimmed) || "yes".equalsIgnoreCase(trimmed); + } + + private static String trimToNull(String value) { + if (value == null) { + return null; + } + + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed; + } + public ManagementServerNode() { setRunLevel(ComponentLifecycle.RUN_LEVEL_FRAMEWORK_BOOTSTRAP); } @@ -111,8 +156,7 @@ public ManagementServerNode() { @Override public void check() { if (s_nodeId <= 0) { - throw new CloudRuntimeException( - "Unable to derive the management server node id (source: " + s_nodeIdSource + ")", s_initError); + throw new CloudRuntimeException("Unable to get the management server node id"); } } @@ -124,10 +168,11 @@ public static long getManagementServerId() { public boolean start() { try { check(); - } catch (CloudRuntimeException e) { - logger.error("System integrity check failed for the management server node id", e); - throw e; + } catch (Exception e) { + logger.error("System integrity check exception", e); + System.exit(1); } + logger.info("Management server node id: {} (source: {})", s_nodeId, s_nodeIdSource); return true; } From cb6daf97fd31c11363a776979fcb9c1e0a1d6adc Mon Sep 17 00:00:00 2001 From: DamonB Date: Fri, 7 Aug 2026 15:28:45 -0500 Subject: [PATCH 3/3] Fixed import for NoSuchAlgorithmException --- .../apache/cloudstack/utils/identity/ManagementServerNode.java | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java b/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java index 43cf3b92f4ba..bb947e7e29f9 100644 --- a/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java +++ b/utils/src/main/java/org/apache/cloudstack/utils/identity/ManagementServerNode.java @@ -22,6 +22,7 @@ import java.net.InetAddress; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.component.ComponentLifecycle;