Note: generated by Claude Code.
This document explains the implementation of a new persistence field in subscription-manager's package profile functionality. This field helps distinguish between different types of package installations on ostree-based systems like image mode machines.
Traditional Linux systems (like regular RHEL, Fedora, Ubuntu) install packages directly onto the filesystem. All packages are essentially "permanent" until explicitly removed.
ostree-based systems work differently:
- They have an immutable base image containing core system packages
- Additional packages can be layered on top or installed transiently
- This creates different "persistence" levels for packages
Before this change, subscription-manager reported all packages the same way, regardless of how they were installed. Subscription services couldn't distinguish between:
- Base image packages - Core packages that are part of the ostree commit
- Layered packages - Packages added permanently via
rpm-ostree install - Transient packages - Packages added temporarily via
dnf --transient install
This information is valuable for understanding system configuration and compliance.
The package profile now includes an optional persistence field with two possible values:
"persistent"- Package is part of the base ostree commit (immutable)"transient"- Package was installed after the base commit (layered or transient)
- Non-ostree systems (traditional Linux): Field is not included (maintains backward compatibility)
- bootc systems: Field is always included for every package
Before (all systems):
{
"name": "vim-enhanced",
"version": "9.1.083",
"release": "6.el10_1",
"arch": "x86_64",
"epoch": 0,
"vendor": "Red Hat, Inc."
}After (ostree systems only):
{
"name": "vim-enhanced",
"version": "9.1.083",
"release": "6.el10_1",
"arch": "x86_64",
"epoch": 0,
"vendor": "Red Hat, Inc.",
"persistence": "transient"
}The implementation follows these steps:
- Detect ostree system - Check if the system is running on ostree using the OSTree library
- Find base commit - Identify the original ostree commit without modifications
- Get base packages - Use
rpm-ostree db listto get packages from the base commit - Compare packages - Compare currently installed packages vs. base commit packages
- Assign persistence - Mark packages as persistent (in base) or transient (added later)
Problem: On bootc systems in "transient unlock" mode, both the current and ostree deployment RPM databases contain identical packages.
Solution: Instead of reading RPM databases directly, use rpm-ostree db list <commit-hash> to get the true base commit packages.
Problem: The booted deployment includes all changes, not just the base packages.
Solution: Use the rollback deployment or find a non-transient deployment as the baseline for comparison.
class Package:
def __init__(self, name, version, release, arch, epoch=0, vendor=None, persistence=None):
# ... existing fields ...
self.persistence = persistence
def to_dict(self):
result = { /* existing fields */ }
# Only include persistence if set (ostree systems only)
if self.persistence is not None:
result["persistence"] = self.persistence
return resultdef _is_ostree_system():
"""Check if running on ostree using OSTree library"""
def _get_immutable_packages():
"""Get base commit packages using rpm-ostree db list"""
def _accumulate_profile(rpm_header_list):
"""Compare current vs base packages to determine persistence"""# Fresh bootc system with only base packages
subscription-manager package-profileResult: All ~480 packages marked as "persistent"
# Install a package transiently
sudo dnf --transient install vim-enhanced
subscription-manager package-profileResult:
- ~480 base packages marked as
"persistent" - vim-enhanced marked as
"transient"
# Regular RHEL system
subscription-manager package-profileResult: No persistence field included (backward compatible)
- Compliance monitoring - Understand which packages are part of the approved base image
- Configuration drift detection - Identify systems that have been modified from the standard
- License tracking - Better understand actual vs. base package usage
- Security auditing - Track non-standard package installations
- Support efficiency - Quickly identify customized vs. standard systems
- Inventory management - Understand true system composition
- Traditional systems continue to work exactly as before
- New field is optional - existing parsing code won't break
- Progressive enhancement - services can gradually adopt the new field
- No behavior changes to existing workflows
- No new dependencies on traditional systems
- Graceful degradation if ostree detection fails
- ✅ Non-ostree systems (no persistence field)
- ✅ Clean ostree systems (all persistent)
- ✅ ostree systems with transient packages (mixed persistence)
- ✅ Error handling (graceful fallback)
- ✅ Package serialization/deserialization
# Check if persistence detection is working
python3 -c "
from rhsm.profile import RPMProfile
profile = RPMProfile()
packages = profile.collect()
persistent = sum(1 for p in packages if p.get('persistence') == 'persistent')
transient = sum(1 for p in packages if p.get('persistence') == 'transient')
print(f'Persistent: {persistent}, Transient: {transient}')
"
Main change: https://github.com/ianballou/subscription-manager/blob/edc811401822d79d6574ff347ce14b04c7ced322/src/rhsm/profile.py