Subscription-manager and dnf5?

I am in the process of rolling out a set of managed workstations. For this we have settled on Fedora, where we control the enabled repositories through a Composite Content View, and an activation key.
Manually including the redhat_register snippet seems to do the job as expected, and once the machine is provisioned, it is correctly registered.

However Fedora has been using dnf5 since version 41 and subscription-manager, to the best of my research, only interfaces with dnf4.
Manually disabling all other other repos then those from the redhat.repo file, makes sure that dnf5 uses the same repos as dnf4 (as disable_system_repo obviously doesn’t have effect when there is no plugin for dnf5).

Fedora still ships with dnf4, which can be called manually, but Ansible now defaults to using dnf5 as long as it is installed, and I am currently unsure of how much will explode down the line, if I just uninstall dnf5 for the time being (e.g., integration with PackageKit and GNOME Software, etc).

Question:

  • Will starting the rhsmcertd service, and having it report package data back to Katello on a regular basis, handle most of what would be missing when using dnf5 as the default dnf through Ansible and and other tools?

  • As far as i can see, then the only other thing that is being missed, is refreshing of the subscription data from Katello. This could temporarily be handled with a timer calling subscription-manager refresh and subscription-manager repos (as refresh doesn’t seem to update the redhat.repo file).

    I also read about the Actions plugin of dnf5, which allows for hooks to be defined. Perhaps the mentioned timer could bed implemented as a dnf5 action instead, such that all dnf actions would first update the subscription data, just as subscription-manager does in dnf4.

  • Have I missed something, that could potentially blow up when using dnf5 with a Katello registered host?

  • Is there any work on a dnf5 plugin of subscription-manager? I have been unable to find any mention of this.

1 Like

Reasearching a bit on the Ansible side, seems that unless using the dnf5 module directly, it will use whatever the /usr/bin/dnf symlink points at. However changing this to dnf4 seems like a temporary fix, as it would be overridden whenever there is an update to the dnf5 package, as far as I can tell.

It is however possible to force dnf4 through the use_backend property, but that would add a lot of special handling that I would much prefer not to do if at all possible.

1 Like

I can answer at least this part. There are 3 ways package profiles can be sent to Katello:

  • if you force-upload it (dnf uploadprofile --force-upload)
  • if package_profile_on_trans is set to 1 in your rhsm configuration, and a dnf(4)? transaction happens
  • every cert_check_interval minutes (default is 240 or 4 hours) via rhsmcertd.

So rhsmcertd will take care of only that last one.

We haven’t really touched dnf5 yet in Katello development, so stay tuned for those changes which will come eventually. But I don’t have much more information than that right now.

2 Likes

I stand a bit corrected. A fresh install seems to only come with dnf5?, it might be my installation of subsciption-manager that pulls in dnf4 through python3-dnf (and friends).

I previously stumbled on the libdnf5-plugin-rhsm package, but didn’t look any closer at it, as it didn’t seem to work with Katello.
Looking further at the code, it clearly backs off if the "/var/lib/rhsm" directory exists (if subscription-manager is installed). So I am unsure how this plugin is supposed to be used. I wasn’t able to find any obvious documentation

Thanks for the insights.

That would indicate that I have to look into whether or not things break, if I replace dnf5 with dnf4.

I just played around with the libdnf5-plugin-actions. The result is still far from perfect, as the actions plugin doesn’t really allow for printing information to the user, so print to stderr in order to get text into the console.

the following works for a dnf repolist, but it also prints the information about how many repos it disabled, when you autocomplete during dnf search. So clearly some more work is needed.

But it seems like a quick and dirty way to hack the few things together that is needed.

/etc/dnf/libdnf5-plugins/actions.d/subscription-manager.actions:

# Disable repos if subscription-manager is configured to control system repos.
repos_configured::::/etc/dnf/libdnf5-plugins/actions.d/subscription-manager-repos-configured.py

/etc/dnf/libdnf5-plugins/actions.d/subscription-manager-repos-configured.py:

#!/usr/bin/env python3

import configparser
import glob
import os
import sys

def eprint(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)


def parse_config(config_file):
    c = configparser.ConfigParser()
    c.read(config_file)
    return c


def main():    
    disable_system_repos=0
    disabled_repos=0
    
    c = parse_config("/etc/dnf/plugins/subscription-manager.conf")
    if c.has_section('main'):
        # If not defined, then assume  system repos should NOT be disabled.
        disable_system_repos = int(c['main'].get('disable_system_repos', "0"))
        
    if not disable_system_repos:
        print("log.INFO=subscription-manager is not managing system repos, exiting...")
        exit(0)
        
    print("log.INFO=subscription-manager is managing system repos, proceeding...")            
    repo_files = glob.glob("/etc/yum.repos.d/*.repo")
    for file in repo_files:
        if os.path.basename(file) == "redhat.repo":
            continue
        
        c = parse_config(file)
        for repo_name in c.sections():
            repo = c[repo_name]
                    
            # XXX: If the 'enabled' property is missing, what do we do? Assume
            # true, so we end up disabling the repo, just in case.
            if int(repo.get("enabled", "1")):
                disabled_repos += 1
                print(f"log.INFO=Disabling repo '{repo_name}' from {os.path.basename(file)} as not in redhat.repo")
                print(f"conf.{repo_name}.enabled=0")

    eprint(f"subscription-manager (action) plugin disabled {disabled_repos} system repositories with respect of configuration in /etc/dnf/plugins/subscription-manager.conf")


if __name__ == "__main__":
    main()
1 Like