fix(linstor): surface ambiguous template fallbacks and legacy template orphans

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-<rscGrpName>" 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.
This commit is contained in:
James Peru 2026-04-29 00:02:28 +03:00
parent 6f4445c5c1
commit 70eed01218
2 changed files with 33 additions and 3 deletions

View File

@ -508,9 +508,20 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
// 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;

View File

@ -491,7 +491,26 @@ public class LinstorUtil {
.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-<rscGrpName>" 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) {