Skip to content

fix(linstor): surface ambiguous template fallbacks and legacy orphan cleanup - #13078

Merged
DaanHoogland merged 3 commits into
apache:mainfrom
jmsperu:feature/linstor-template-hygiene
May 27, 2026
Merged

fix(linstor): surface ambiguous template fallbacks and legacy orphan cleanup#13078
DaanHoogland merged 3 commits into
apache:mainfrom
jmsperu:feature/linstor-template-hygiene

Conversation

@jmsperu

Copy link
Copy Markdown
Collaborator

Summary

Two small visibility improvements to the LINSTOR template-handling path. Both preserve existing behaviour and only add log output that surfaces conditions operators have currently no easy way to detect.

1. `LinstorUtil.findResourceDefinition` — log on ambiguous fallback

When clone-from-template runs, the method scans for a resource whose name starts with the template prefix AND whose aux properties include `_cs-template-for-`. If no exact-property match exists, the method falls back to the first matching resource by name and returns it silently.

In setups with multiple resource groups on the same controller (or with legacy templates cached before the ref-count convention was added), this fallback can return a template that belongs to the wrong resource group, and the clone produces an unexpected result.

This change keeps the fallback behaviour but logs a WARN naming:

  • the requested resource name and resource group
  • the fallback resource that was chosen
  • the actual `_cs-template-for-*` aux properties present on the fallback (so operators can see what RGs do claim it)

2. `LinstorStorageAdaptor.deRefOrDeleteResource` — log on legacy-template orphan cleanup

The ref-count branch already deletes resources that have zero remaining `_cs-template-for-` aux properties. Two conditions reach this branch:

  • a normal resource (no template-for properties expected) — already commented in code
  • a legacy template with no template-for properties (predates the ref-count convention)

Currently both look identical in the logs. Operators upgrading from older versions can't tell how many orphan legacy templates were cleaned up.

This change logs an INFO line in the second case, identifying the resource as a legacy template and naming the resource group context. Behaviour unchanged.

Test plan

  • CI build + unit tests
  • Manual: deploy a VM from template in a single-RG setup — no new logs
  • Manual: deploy a VM from template in a multi-RG setup where the template lacks the exact aux property — observe the new WARN
  • Manual: evict a legacy template (no aux properties) — observe the new INFO line during cleanup

Why three small PRs

Per the suggestion from earlier review, splitting LINSTOR plugin improvements into focused PRs:

All three target the LINSTOR primary-storage plugin and can be reviewed independently.

…e 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.
@sureshanaparti

Copy link
Copy Markdown
Contributor

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@sureshanaparti a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves operator visibility in the LINSTOR template-handling path by adding targeted log lines around (1) ambiguous template selection fallback and (2) legacy/orphan template cleanup, while intending to preserve existing behavior.

Changes:

  • Add a WARN log in LinstorUtil.findResourceDefinition when falling back to the first name-prefix match due to missing expected _cs-template-for-<rscGrp> aux property.
  • Add an INFO log in LinstorStorageAdaptor.deRefOrDeleteResource intended to surface deletion of legacy templates that have zero _cs-template-for- aux properties.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

FileDescription
plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.javaAdds WARN logging to make ambiguous template fallback selection visible (includes chosen fallback and related aux keys).
plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.javaAdds INFO logging intended to distinguish legacy-template orphan cleanup during deref/delete logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +515 to +524
// 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);
}

CopilotAIApr 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new INFO log for “legacy template predating the ref-count convention” will currently fire for all non-template resource deletions too. existingRDs is already built via getRDListStartingWith(api, rscName), so rd.getName().toLowerCase().startsWith(rscName.toLowerCase()) is always true; whenever expectedProps == 0 (normal volume/resource with no _cs-template-for- props), this will emit the legacy-template message and add significant log noise/misleading attribution. Tighten the condition so it only matches the intended legacy-template-orphan case (e.g., by using a reliable template discriminator, passing an isTemplate flag from the caller, or checking for a template-specific naming/property convention), and avoid logging for ordinary resource deletions.

Suggested change
// 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);
}

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copilot has a point here. the extra .startWith, will always be true, as the existingRD will exactly only contain resources only such resources.

@codecov

codecovBot commented Apr 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 18.10%. Comparing base (6f4445c) to head (4ee8d94).
⚠️ Report is 56 commits behind head on main.

Files with missing linesPatch %Lines
...cloudstack/storage/datastore/util/LinstorUtil.java0.00%11 Missing ⚠️
.../hypervisor/kvm/storage/LinstorStorageAdaptor.java0.00%6 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #13078 +/- ##
============================================
+ Coverage 18.02% 18.10% +0.07% - Complexity 16621 16750 +129 
============================================
Files 6029 6037 +8 Lines 542184 542946 +762 Branches 66451 66489 +38 ============================================
+ Hits 97740 98304 +564 - Misses 433428 433592 +164 - Partials 11016 11050 +34 
FlagCoverage Δ
uitests3.51% <ø> (-0.01%)⬇️
unittests19.27% <0.00%> (+0.08%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 17649

@DaanHoogland

Copy link
Copy Markdown
Contributor

@rp- , will you have a look at this?

@rp-

rp- commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

@rp- , will you have a look at this?

yes

@rp-rp- left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably remove the unnecessary .startsWith() condition.
I guess I don't mind about the too much logging part.
Functional my tests ran through

Comment on lines +515 to +524
// 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);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copilot has a point here. the extra .startWith, will always be true, as the existingRD will exactly only contain resources only such resources.

existingRDs already filtered by getRDListStartingWith(rscName), so the
.startsWith() guard is always true. Per @rp- review on PR apache#13078.
@jmsperu

Copy link
Copy Markdown
CollaboratorAuthor

@rp- thanks — pushed eff9b09d51 dropping the redundant .startsWith() check. Logging stays as before for the legacy/orphan case.

@sureshanaparti

Copy link
Copy Markdown
Contributor

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@sureshanaparti a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 17800

rp-
rp- approved these changes May 11, 2026

@rp-rp- left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@sureshanaparti

Copy link
Copy Markdown
Contributor

@blueorangutan test

@blueorangutan

Copy link
Copy Markdown

@sureshanaparti a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-16066)
Environment: kvm-ol8 (x2), zone: Advanced Networking with Mgmt server ol8
Total time taken: 84675 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr13078-t16066-kvm-ol8.zip
Smoke tests completed. 146 look OK, 5 have errors, 0 did not run
Only failed and skipped tests results shown below:

TestResultTime (s)Test File
test_vm_backup_create_vm_from_backupFailure607.86test_backup_recovery_nas.py
test_vm_backup_lifecycleError0.12test_backup_recovery_nas.py
ContextSuite context=TestSharedFSLifecycle>:setupError0.00test_sharedfs_lifecycle.py
test_10_attachAndDetach_isoFailure607.15test_vm_life_cycle.py
test_01_create_vm_snapshotsFailure606.87test_vm_snapshots.py
test_02_revert_vm_snapshotsFailure600.75test_vm_snapshots.py
test_03_delete_vm_snapshotsFailure0.04test_vm_snapshots.py
test_01_create_volumeFailure610.36test_volumes.py
test_01_root_volume_encryptionFailure716.76test_volumes.py
test_02_data_volume_encryptionFailure641.91test_volumes.py
test_03_root_and_data_volume_encryptionFailure663.97test_volumes.py
test_02_attach_volumeFailure1271.46test_volumes.py
test_02_attach_volumeFailure1271.48test_volumes.py
test_03_download_attached_volumeFailure666.67test_volumes.py
test_04_delete_attached_volumeFailure665.75test_volumes.py
test_05_detach_volumeFailure753.81test_volumes.py
test_06_download_detached_volumeFailure846.55test_volumes.py
test_07_resize_failFailure663.69test_volumes.py
test_08_resize_volumeFailure670.11test_volumes.py
test_09_delete_detached_volumeFailure664.85test_volumes.py
test_10_list_volumesFailure661.43test_volumes.py
test_11_attach_volume_with_unstarted_vmFailure770.59test_volumes.py
test_12_resize_volume_with_only_size_parameterFailure677.40test_volumes.py
test_13_migrate_volume_and_change_offeringFailure808.59test_volumes.py
test_14_delete_volume_delete_protectionFailure663.94test_volumes.py

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 17922

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian Build Failed (tid-16130)

@jmsperu

Copy link
Copy Markdown
CollaboratorAuthor

Hi @sureshanaparti@DaanHoogland — the Trillian build failed on the 2026-05-20 run (tid-16130) after packaging succeeded on el8/el9/el10/debian/suse15, so this looks like a transient infra issue rather than anything in the patch. The previous Trillian run (tid-16066, 2026-05-12) had 146 OK / 5 environmental failures. Could one of you re-trigger with @blueorangutan test when convenient? Approval from @rp- still stands from 2026-05-11. Happy to push a no-op commit if that's easier. Thanks!

One-word refinement — 'first-match selection' -> 'first-match fallback'
to align with the local 'fallback' variable name and the surrounding
prose. No behavioural change. Force-triggers a fresh CI run on this PR
to clear the stale 2026-05-09 component-build failure that's been
keeping mergeStateStatus at UNSTABLE.
@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-16156)
Environment: kvm-ol8 (x2), zone: Advanced Networking with Mgmt server ol8
Total time taken: 51440 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr13078-t16156-kvm-ol8.zip
Smoke tests completed. 151 look OK, 0 have errors, 0 did not run
Only failed and skipped tests results shown below:

TestResultTime (s)Test File

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 18036

@DaanHoogland

Copy link
Copy Markdown
Contributor

@blueorangutan test

@blueorangutan

Copy link
Copy Markdown

@DaanHoogland a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-16184)
Environment: kvm-ol8 (x2), zone: Advanced Networking with Mgmt server ol8
Total time taken: 55268 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr13078-t16184-kvm-ol8.zip
Smoke tests completed. 149 look OK, 2 have errors, 0 did not run
Only failed and skipped tests results shown below:

TestResultTime (s)Test File
test_02_list_cpvm_vmFailure0.04test_ssvm.py
test_04_cpvm_internalsFailure0.04test_ssvm.py
test_10_reboot_cpvm_forcedError9.32test_ssvm.py
test_01_redundant_vpc_site2site_vpnFailure416.51test_vpc_vpn.py
test_01_redundant_vpc_site2site_vpnError416.55test_vpc_vpn.py

@DaanHoogland
DaanHoogland merged commit 6786d45 into apache:mainMay 27, 2026
26 of 27 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@jmsperu@sureshanaparti@blueorangutan@DaanHoogland@rp-@winterhazel