From 70eed01218e4a8395f3fcf4ae7eec11e052597fd Mon Sep 17 00:00:00 2001 From: James Peru Date: Wed, 29 Apr 2026 00:02:28 +0300 Subject: [PATCH] fix(linstor): surface ambiguous template fallbacks and legacy template orphans MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two small visibility improvements that make existing template behaviour easier to audit, especially after upgrading from versions that predated the ref-count convention. LinstorUtil.findResourceDefinition When no resource has the exact "_cs-template-for-" Aux property, the method silently returned the first resource whose name starts with the requested prefix. With multiple resource groups on a single controller, this can route a clone to the wrong template. Now logs a WARN naming the requested rscGrpName, the fallback resource, and the actual aux properties present. Behaviour unchanged: still returns the first match. Operators can act on the warning by setting the property explicitly or removing the unrelated definition. LinstorStorageAdaptor.deRefOrDeleteResource When deleting a resource that has zero `_cs-template-for-` aux properties AND whose name starts with the template-name prefix the caller is acting on, log an INFO line. These are legacy templates cached before the ref-count convention was introduced — they get picked up by the existing "if expectedProps == 0" branch and deleted. Surfacing them lets operators see how many orphans existed at upgrade time and confirm the cleanup happened. --- .../kvm/storage/LinstorStorageAdaptor.java | 15 +++++++++++-- .../storage/datastore/util/LinstorUtil.java | 21 ++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index 2b11c83c8021..af2c508ff0e9 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -508,9 +508,20 @@ private boolean deRefOrDeleteResource(DevelopersApi api, String rscName, String // if there is only one template-for property left for templates, the template isn't needed anymore // or if it isn't a template anyway, it will not have this Aux property // _cs-template-for- properties work like a ref-count. - if (rd.getProps().keySet().stream() + long remainingTemplateRefs = rd.getProps().keySet().stream() .filter(key -> key.startsWith("Aux/" + LinstorUtil.CS_TEMPLATE_FOR_PREFIX)) - .count() == expectedProps) { + .count(); + if (remainingTemplateRefs == expectedProps) { + // Surface the case where a resource that LOOKS like a template (resource name + // starts with the requested prefix) has zero `_cs-template-for-` aux properties + // even though we never decremented one — that's a legacy template predating the + // ref-count convention. Logging it before deletion lets operators audit how + // many such orphans existed at upgrade time. + if (expectedProps == 0 && rd.getName().toLowerCase().startsWith(rscName.toLowerCase())) { + logger.info("Linstor: deleting resource {} which has no _cs-template-for- aux properties " + + "(legacy template predating the ref-count convention, or a stale orphan). " + + "Resource group context: {}", rd.getName(), rscGrpName); + } ApiCallRcList answers = api.resourceDefinitionDelete(rd.getName()); checkLinstorAnswersThrow(answers); deleted = true; diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java index 7c45493dddc4..ddd4ffe11e9e 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java @@ -491,7 +491,26 @@ public static ResourceDefinition findResourceDefinition(DevelopersApi api, Strin .filter(rscDfn -> rscDfn.getProps().containsKey(LinstorUtil.getTemplateForAuxPropKey(rscGrpName))) .findFirst(); - return rd.orElseGet(() -> rdsStartingWith.get(0)); + if (rd.isPresent()) { + return rd.get(); + } + // Fallback: no resource has the exact "_cs-template-for-" property. + // This happens when (a) the matched resource is a legacy template created before that + // convention was introduced, or (b) the template was cached by a different resource + // group and the operator hopes to share it. Log so the ambiguity is visible — silent + // first-match selection has previously routed clones to the wrong template when + // multiple resource groups coexisted on the same controller. + ResourceDefinition fallback = rdsStartingWith.get(0); + LOGGER.warn("LINSTOR findResourceDefinition: no resource for '{}' has the expected " + + "Aux property '{}' for resource group '{}'; falling back to first match '{}' " + + "(present aux properties: {}). If this is wrong, set the property explicitly " + + "or remove the unrelated resource definition.", + rscName, getTemplateForAuxPropKey(rscGrpName), rscGrpName, + fallback.getName(), + fallback.getProps().keySet().stream() + .filter(k -> k.startsWith("Aux/" + CS_TEMPLATE_FOR_PREFIX)) + .collect(Collectors.toList())); + return fallback; } public static boolean isRscDiskless(ResourceWithVolumes rsc) {