From 1aafb7317df1f18934aeafc2222296c037ee2e43 Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Mon, 27 Apr 2026 12:06:10 -0400 Subject: [PATCH 1/7] CLOS-4056: gate CLN-assuming actors on is_cln_configured() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Systems migrated to the no-auth (SWNG) scheme no longer have CLN as a package source. Several CL-specific actors assumed CLN was always active and either crashed on missing files or produced spurious inhibitors. Add cln_detect.is_cln_configured() — True when the CLN plumbing is present and not explicitly disabled (registration file exists + spacewalk plugin installed + plugin enabled). Gate these actors on it: - switch_cln_channel: skip the cln-switch-channel call on no-auth systems. Also downgrade the failed-switch inhibitor to a MEDIUM report, since a failure on a transitional system where CLN plumbing lingers but is no longer usable should not block the upgrade — CL9 packages come from cl-channel / cloudlinux9-baseos instead. - pin_cln_mirror / unpin_cln_mirror: no-op on no-auth systems; also wrap the up2date update in try/except in pin_cln_mirror in case the file was not shipped on the target. - check_rhn_version_override / reset_rhn_version_override: skip on no-auth systems and fall back cleanly when /etc/sysconfig/rhn/up2date is missing. reset_rhn_version_override: also fix a pre-existing bug where rebinding inside the loop did not update config_data, so the reset was silently a no-op. enable_yum_spacewalk_plugin is not touched here — CLOS-3960 has concurrent work on it. --- .../actors/checkrhnversionoverride/actor.py | 52 ++++++++----- .../cloudlinux/actors/pinclnmirror/actor.py | 19 ++++- .../actors/resetrhnversionoverride/actor.py | 29 ++++++-- .../actors/switchclnchannel/actor.py | 29 ++++++-- .../cloudlinux/actors/unpinclnmirror/actor.py | 5 ++ .../cloudlinux/libraries/cln_detect.py | 55 ++++++++++++++ .../libraries/tests/test_cln_detect.py | 74 +++++++++++++++++++ 7 files changed, 229 insertions(+), 34 deletions(-) create mode 100644 repos/system_upgrade/cloudlinux/libraries/cln_detect.py create mode 100644 repos/system_upgrade/cloudlinux/libraries/tests/test_cln_detect.py diff --git a/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py b/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py index 6a21e10b6e..a9786d143e 100644 --- a/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py @@ -1,8 +1,10 @@ from leapp.actors import Actor from leapp import reporting +from leapp.libraries.stdlib import api from leapp.reporting import Report from leapp.tags import ChecksPhaseTag, IPUWorkflowTag from leapp.libraries.common.cllaunch import run_on_cloudlinux +from leapp.libraries.common.cln_detect import is_cln_configured class CheckRhnVersionOverride(Actor): @@ -17,23 +19,35 @@ class CheckRhnVersionOverride(Actor): @run_on_cloudlinux def process(self): + if not is_cln_configured(): + # CLOS-4056: no-auth systems have no CLN config to inspect. + return + up2date_config = '/etc/sysconfig/rhn/up2date' - with open(up2date_config, 'r') as f: - config_data = f.readlines() - for line in config_data: - if line.startswith('versionOverride='): - stripped_line = line.strip().split("=") - versionOverrideValue = stripped_line[1] - # If the version is being overriden to 8, we can continue as is. - if versionOverrideValue not in ['', '8']: - title = 'RHN up2date: versionOverride overwritten by the upgrade' - summary = ("The RHN config file up2date has a set value of the versionOverride option: {}." - " This value will get overwritten by the upgrade process, and reset to an empty" - " value once it's complete.".format(versionOverrideValue)) - reporting.create_report([ - reporting.Title(title), - reporting.Summary(summary), - reporting.Severity(reporting.Severity.MEDIUM), - reporting.Groups([reporting.Groups.OS_FACTS]), - reporting.RelatedResource('file', '/etc/sysconfig/rhn/up2date') - ]) + try: + with open(up2date_config, 'r') as f: + config_data = f.readlines() + except (OSError, IOError): + api.current_logger().info( + "RHN up2date config %s not present; skipping versionOverride check", + up2date_config, + ) + return + + for line in config_data: + if line.startswith('versionOverride='): + stripped_line = line.strip().split("=") + versionOverrideValue = stripped_line[1] + # If the version is being overriden to 8, we can continue as is. + if versionOverrideValue not in ['', '8']: + title = 'RHN up2date: versionOverride overwritten by the upgrade' + summary = ("The RHN config file up2date has a set value of the versionOverride option: {}." + " This value will get overwritten by the upgrade process, and reset to an empty" + " value once it's complete.".format(versionOverrideValue)) + reporting.create_report([ + reporting.Title(title), + reporting.Summary(summary), + reporting.Severity(reporting.Severity.MEDIUM), + reporting.Groups([reporting.Groups.OS_FACTS]), + reporting.RelatedResource('file', '/etc/sysconfig/rhn/up2date') + ]) diff --git a/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py b/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py index bc1686233f..162dbba96d 100644 --- a/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py @@ -4,6 +4,7 @@ from leapp.actors import Actor from leapp.libraries.stdlib import api from leapp.libraries.common.cllaunch import run_on_cloudlinux +from leapp.libraries.common.cln_detect import is_cln_configured from leapp.libraries.common.cln_switch import get_target_userspace_path from leapp.tags import DownloadPhaseTag, IPUWorkflowTag from leapp.libraries.common.config.version import get_target_major_version @@ -25,6 +26,13 @@ class PinClnMirror(Actor): @run_on_cloudlinux def process(self): """Pin CLN mirror""" + if not is_cln_configured(): + # CLOS-4056: no-auth systems don't use CLN mirrors; skip cleanly. + api.current_logger().info( + "CLN is not configured on this system; skipping mirror pinning" + ) + return + target_userspace = get_target_userspace_path() api.current_logger().info("Pin CLN mirror: target userspace=%s", target_userspace) @@ -54,6 +62,11 @@ def process(self): api.current_logger().info("Pin CLN mirror %s in %s", mirror_url, mirrorlist_path) up2date_path = os.path.join(target_userspace, 'etc/sysconfig/rhn/up2date') - with open(up2date_path, 'a+') as file: - file.write('\nmirrorURL[comment]=Set mirror URL to /etc/mirrorlist\nmirrorURL=file:///etc/mirrorlist\n') - api.current_logger().info("Updated up2date_path %s", up2date_path) + try: + with open(up2date_path, 'a+') as file: + file.write('\nmirrorURL[comment]=Set mirror URL to /etc/mirrorlist\nmirrorURL=file:///etc/mirrorlist\n') + api.current_logger().info("Updated up2date_path %s", up2date_path) + except (OSError, IOError) as e: + api.current_logger().info( + "Could not update %s: %s", up2date_path, e, + ) diff --git a/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py b/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py index 21b2164cb0..7b5f5d0f51 100644 --- a/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py @@ -1,6 +1,8 @@ from leapp.actors import Actor +from leapp.libraries.stdlib import api from leapp.tags import FinalizationPhaseTag, IPUWorkflowTag from leapp.libraries.common.cllaunch import run_on_cloudlinux +from leapp.libraries.common.cln_detect import is_cln_configured class ResetRhnVersionOverride(Actor): @@ -15,11 +17,26 @@ class ResetRhnVersionOverride(Actor): @run_on_cloudlinux def process(self): + if not is_cln_configured(): + # CLOS-4056: no-auth systems have no CLN config to reset. + return + up2date_config = '/etc/sysconfig/rhn/up2date' - with open(up2date_config, 'r') as f: - config_data = f.readlines() - for line in config_data: - if line.startswith('versionOverride='): - line = 'versionOverride=' + try: + with open(up2date_config, 'r') as f: + config_data = f.readlines() + except (OSError, IOError): + api.current_logger().info( + "RHN up2date config %s not present; skipping versionOverride reset", + up2date_config, + ) + return + + new_data = [] + for line in config_data: + if line.startswith('versionOverride='): + new_data.append('versionOverride=\n') + else: + new_data.append(line) with open(up2date_config, 'w') as f: - f.writelines(config_data) + f.writelines(new_data) diff --git a/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py b/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py index 86421856b2..e80f0e8fa6 100644 --- a/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py @@ -3,7 +3,8 @@ from leapp.tags import FirstBootPhaseTag, IPUWorkflowTag from leapp.libraries.stdlib import CalledProcessError from leapp.libraries.common.cllaunch import run_on_cloudlinux -from leapp.libraries.common.cln_switch import cln_switch, get_target_userspace_path +from leapp.libraries.common.cln_detect import is_cln_configured +from leapp.libraries.common.cln_switch import cln_switch from leapp import reporting from leapp.reporting import Report from leapp.libraries.common.config.version import get_target_major_version @@ -22,9 +23,22 @@ class SwitchClnChannel(Actor): @run_on_cloudlinux def process(self): + if not is_cln_configured(): + # CLOS-4056: No-auth (SWNG) systems have no CLN plumbing. Skipping + # the channel switch here is correct — the system receives CL9 + # packages via cl-channel / cloudlinux9-baseos instead. + api.current_logger().info( + "CLN is not configured on this system; skipping channel switch" + ) + return + try: cln_switch(target=int(get_target_major_version())) except CalledProcessError as e: + # CLOS-4056: Do not inhibit. CLN may be partially configured (legacy + # registration files present but no working registration) on systems + # transitioning to the no-auth scheme, and a failed channel switch + # there is expected — the no-auth repos still serve CL9 packages. reporting.create_report( [ reporting.Title( @@ -33,17 +47,20 @@ def process(self): reporting.Summary( "Command {} failed with exit code {}." " The most probable cause of that is a problem with this system's" - " CloudLinux Network registration.".format(e.command, e.exit_code) + " CloudLinux Network registration. If this system now uses the" + " no-auth (SWNG) repository scheme, this failure is harmless —" + " CL9 packages come from cl-channel / cloudlinux9-baseos instead" + " of CLN.".format(e.command, e.exit_code) ), reporting.Remediation( - hint="Check the state of this system's registration with \'rhn_check\'." - " Attempt to re-register the system with \'rhnreg_ks --force\'." + hint="If you rely on CLN: check registration with 'rhn_check' and" + " re-register with 'rhnreg_ks --force'. If you have migrated to" + " no-auth repos, this message can be ignored." ), - reporting.Severity(reporting.Severity.HIGH), + reporting.Severity(reporting.Severity.MEDIUM), reporting.Groups( [reporting.Groups.OS_FACTS, reporting.Groups.AUTHENTICATION] ), - reporting.Groups([reporting.Groups.INHIBITOR]), ] ) except OSError as e: diff --git a/repos/system_upgrade/cloudlinux/actors/unpinclnmirror/actor.py b/repos/system_upgrade/cloudlinux/actors/unpinclnmirror/actor.py index 8e7ffc93a0..cc2247d91e 100644 --- a/repos/system_upgrade/cloudlinux/actors/unpinclnmirror/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/unpinclnmirror/actor.py @@ -2,6 +2,7 @@ from leapp.actors import Actor from leapp.libraries.common.cllaunch import run_on_cloudlinux +from leapp.libraries.common.cln_detect import is_cln_configured from leapp.libraries.common.cln_switch import get_target_userspace_path from leapp.tags import FirstBootPhaseTag, IPUWorkflowTag @@ -19,6 +20,10 @@ class UnpinClnMirror(Actor): @run_on_cloudlinux def process(self): + if not is_cln_configured(): + # CLOS-4056: pinclnmirror was skipped on no-auth systems, nothing to unpin. + return + target_userspace = get_target_userspace_path() mirrorlist_path = os.path.join(target_userspace, 'etc/mirrorlist') diff --git a/repos/system_upgrade/cloudlinux/libraries/cln_detect.py b/repos/system_upgrade/cloudlinux/libraries/cln_detect.py new file mode 100644 index 0000000000..623f36a71a --- /dev/null +++ b/repos/system_upgrade/cloudlinux/libraries/cln_detect.py @@ -0,0 +1,55 @@ +"""Detection helpers for CLN (CloudLinux Network / Spacewalk) state. + +A system is considered to have CLN *configured* when it has registration +state plus the spacewalk DNF/YUM plugin installed and not explicitly +disabled. Systems that have been migrated to the no-auth (SWNG mirrorlist) +scheme have either: + + - no `/etc/sysconfig/rhn/systemid` (never registered or deregistered), + - no spacewalk plugin installed (rhn-client-tools >= 3.0.1 removes it), or + - the plugin's `enabled = 0` in its config. + +CLOS-4056: several CloudLinux-specific actors were written when CLN was the +only scheme and assume it is always active. They need to gate their +behavior on `is_cln_configured()` so systems on the no-auth scheme pass +through without bogus inhibitors or crashes. +""" + +import os + + +RHN_SYSTEMID = '/etc/sysconfig/rhn/systemid' +SPACEWALK_DNF_CONF = '/etc/dnf/plugins/spacewalk.conf' +SPACEWALK_YUM_CONF = '/etc/yum/pluginconf.d/spacewalk.conf' + + +def _plugin_explicitly_disabled(conf_path): + try: + with open(conf_path) as f: + for line in f: + stripped = line.strip().lower() + if not stripped or stripped.startswith('#') or stripped.startswith('['): + continue + if stripped.startswith('enabled') and '=' in stripped: + value = stripped.split('=', 1)[1].strip() + return value == '0' + except (OSError, IOError): + pass + return False + + +def is_cln_configured(): + """Return True if CLN plumbing is present and not disabled on this system.""" + if not os.path.exists(RHN_SYSTEMID): + return False + + configs = [p for p in (SPACEWALK_DNF_CONF, SPACEWALK_YUM_CONF) if os.path.exists(p)] + if not configs: + return False + + # If any plugin config explicitly disables the plugin, treat as no-auth. + for conf in configs: + if _plugin_explicitly_disabled(conf): + return False + + return True diff --git a/repos/system_upgrade/cloudlinux/libraries/tests/test_cln_detect.py b/repos/system_upgrade/cloudlinux/libraries/tests/test_cln_detect.py new file mode 100644 index 0000000000..ee2bbae411 --- /dev/null +++ b/repos/system_upgrade/cloudlinux/libraries/tests/test_cln_detect.py @@ -0,0 +1,74 @@ +import os + +import pytest + +from leapp.libraries.common import cln_detect + + +@pytest.fixture +def clean_paths(monkeypatch, tmp_path): + """Point cln_detect at a clean tmp dir so each test starts from no state.""" + systemid = tmp_path / "systemid" + dnf_conf = tmp_path / "dnf_spacewalk.conf" + yum_conf = tmp_path / "yum_spacewalk.conf" + monkeypatch.setattr(cln_detect, "RHN_SYSTEMID", str(systemid)) + monkeypatch.setattr(cln_detect, "SPACEWALK_DNF_CONF", str(dnf_conf)) + monkeypatch.setattr(cln_detect, "SPACEWALK_YUM_CONF", str(yum_conf)) + return {"systemid": systemid, "dnf_conf": dnf_conf, "yum_conf": yum_conf} + + +def _touch(path, content=""): + path.write_text(content) + + +def test_no_systemid_means_no_cln(clean_paths): + # Without /etc/sysconfig/rhn/systemid the system is not registered with CLN. + assert cln_detect.is_cln_configured() is False + + +def test_systemid_but_no_plugin_means_no_cln(clean_paths): + _touch(clean_paths["systemid"]) + assert cln_detect.is_cln_configured() is False + + +def test_systemid_and_enabled_dnf_plugin_means_cln(clean_paths): + _touch(clean_paths["systemid"]) + _touch(clean_paths["dnf_conf"], "[main]\nenabled = 1\n") + assert cln_detect.is_cln_configured() is True + + +def test_explicit_disabled_dnf_plugin_means_no_cln(clean_paths): + _touch(clean_paths["systemid"]) + _touch(clean_paths["dnf_conf"], "[main]\nenabled = 0\n") + assert cln_detect.is_cln_configured() is False + + +def test_explicit_disabled_yum_plugin_means_no_cln(clean_paths): + _touch(clean_paths["systemid"]) + _touch(clean_paths["yum_conf"], "[main]\nenabled=0\n") + assert cln_detect.is_cln_configured() is False + + +def test_one_plugin_disabled_one_not_means_no_cln(clean_paths): + # If either plugin config disables it, CLN is not usable. + _touch(clean_paths["systemid"]) + _touch(clean_paths["dnf_conf"], "[main]\nenabled = 1\n") + _touch(clean_paths["yum_conf"], "[main]\nenabled = 0\n") + assert cln_detect.is_cln_configured() is False + + +def test_plugin_conf_without_enabled_key_means_cln(clean_paths): + # A plugin config that doesn't mention `enabled` defaults to enabled upstream, + # so we must treat it as CLN active. + _touch(clean_paths["systemid"]) + _touch(clean_paths["dnf_conf"], "[main]\ntimeout = 120\n") + assert cln_detect.is_cln_configured() is True + + +def test_comments_and_blank_lines_ignored(clean_paths): + _touch(clean_paths["systemid"]) + _touch( + clean_paths["dnf_conf"], + "# some comment\n\n[main]\n# enabled = 0\nenabled = 1\n", + ) + assert cln_detect.is_cln_configured() is True From d7eb9963402bbf02ef41516b3d7d57b5e05539ab Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Mon, 27 Apr 2026 12:06:10 -0400 Subject: [PATCH 2/7] =?UTF-8?q?CLOS-4056:=20clarify=20CLN=20detection=20?= =?UTF-8?q?=E2=80=94=20package=20channel=20vs=20registration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first commit on this branch named the helper is_cln_configured() and the actor comments said things like 'CLN is not configured here'. That conflated two separate concerns and was misleading: CLN registration is still in use on no-auth (SWNG) systems for licensing and inventory — what the no-auth migration changes is only the package channel (the spacewalk DNF plugin no longer delivers packages from a CLN channel). Rename: is_cln_configured() -> is_cln_package_channel_active() so the function name reflects what the actors actually need to gate on, and rewrite its docstring + module header to spell out the package-channel vs registration distinction. Update each of the five actor comments accordingly. The detection logic itself is unchanged — registration state plus a non-disabled spacewalk plugin remains the right heuristic for 'CLN is delivering packages'. Tests updated to match the new name and to phrase scenarios in terms of the channel rather than 'CLN configured'. --- .../actors/checkrhnversionoverride/actor.py | 10 ++-- .../cloudlinux/actors/pinclnmirror/actor.py | 11 ++-- .../actors/resetrhnversionoverride/actor.py | 9 ++-- .../actors/switchclnchannel/actor.py | 22 ++++---- .../cloudlinux/actors/unpinclnmirror/actor.py | 8 +-- .../cloudlinux/libraries/cln_detect.py | 50 +++++++++++++------ .../libraries/tests/test_cln_detect.py | 39 ++++++++------- 7 files changed, 92 insertions(+), 57 deletions(-) diff --git a/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py b/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py index a9786d143e..21a8b3ee13 100644 --- a/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py @@ -4,7 +4,7 @@ from leapp.reporting import Report from leapp.tags import ChecksPhaseTag, IPUWorkflowTag from leapp.libraries.common.cllaunch import run_on_cloudlinux -from leapp.libraries.common.cln_detect import is_cln_configured +from leapp.libraries.common.cln_detect import is_cln_package_channel_active class CheckRhnVersionOverride(Actor): @@ -19,8 +19,12 @@ class CheckRhnVersionOverride(Actor): @run_on_cloudlinux def process(self): - if not is_cln_configured(): - # CLOS-4056: no-auth systems have no CLN config to inspect. + if not is_cln_package_channel_active(): + # CLOS-4056: versionOverride only matters when CLN is delivering + # packages — the upgrade rewrites it to drive channel selection. + # On no-auth (SWNG) systems the package channel is cl-channel, + # not CLN, so there is nothing to inspect or warn about even if + # /etc/sysconfig/rhn/up2date is still present from registration. return up2date_config = '/etc/sysconfig/rhn/up2date' diff --git a/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py b/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py index 162dbba96d..1bdcb42d9e 100644 --- a/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py @@ -4,7 +4,7 @@ from leapp.actors import Actor from leapp.libraries.stdlib import api from leapp.libraries.common.cllaunch import run_on_cloudlinux -from leapp.libraries.common.cln_detect import is_cln_configured +from leapp.libraries.common.cln_detect import is_cln_package_channel_active from leapp.libraries.common.cln_switch import get_target_userspace_path from leapp.tags import DownloadPhaseTag, IPUWorkflowTag from leapp.libraries.common.config.version import get_target_major_version @@ -26,10 +26,13 @@ class PinClnMirror(Actor): @run_on_cloudlinux def process(self): """Pin CLN mirror""" - if not is_cln_configured(): - # CLOS-4056: no-auth systems don't use CLN mirrors; skip cleanly. + if not is_cln_package_channel_active(): + # CLOS-4056: pinning the CLN mirror is only meaningful when CLN + # is delivering packages. On no-auth (SWNG) systems packages come + # from cl-channel via mirrorlist, so there is nothing to pin — + # registration may still be in place but is irrelevant here. api.current_logger().info( - "CLN is not configured on this system; skipping mirror pinning" + "CLN is not the active package channel; skipping mirror pinning" ) return diff --git a/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py b/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py index 7b5f5d0f51..ff446c77a2 100644 --- a/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py @@ -2,7 +2,7 @@ from leapp.libraries.stdlib import api from leapp.tags import FinalizationPhaseTag, IPUWorkflowTag from leapp.libraries.common.cllaunch import run_on_cloudlinux -from leapp.libraries.common.cln_detect import is_cln_configured +from leapp.libraries.common.cln_detect import is_cln_package_channel_active class ResetRhnVersionOverride(Actor): @@ -17,8 +17,11 @@ class ResetRhnVersionOverride(Actor): @run_on_cloudlinux def process(self): - if not is_cln_configured(): - # CLOS-4056: no-auth systems have no CLN config to reset. + if not is_cln_package_channel_active(): + # CLOS-4056: versionOverride is only set/used by the CLN package + # channel flow. If the system isn't on CLN for packages, leave + # /etc/sysconfig/rhn/up2date alone — registration metadata there + # is not ours to touch. return up2date_config = '/etc/sysconfig/rhn/up2date' diff --git a/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py b/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py index e80f0e8fa6..f7c5675c82 100644 --- a/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py @@ -3,7 +3,7 @@ from leapp.tags import FirstBootPhaseTag, IPUWorkflowTag from leapp.libraries.stdlib import CalledProcessError from leapp.libraries.common.cllaunch import run_on_cloudlinux -from leapp.libraries.common.cln_detect import is_cln_configured +from leapp.libraries.common.cln_detect import is_cln_package_channel_active from leapp.libraries.common.cln_switch import cln_switch from leapp import reporting from leapp.reporting import Report @@ -23,22 +23,24 @@ class SwitchClnChannel(Actor): @run_on_cloudlinux def process(self): - if not is_cln_configured(): - # CLOS-4056: No-auth (SWNG) systems have no CLN plumbing. Skipping - # the channel switch here is correct — the system receives CL9 - # packages via cl-channel / cloudlinux9-baseos instead. + if not is_cln_package_channel_active(): + # CLOS-4056: CLN is no longer the package channel here (no-auth / + # SWNG mode). Skipping the channel switch is correct — packages + # come from cl-channel / cloudlinux9-baseos instead. The system + # may still be CLN-registered for licensing; that is a separate + # concern this actor does not need to manage. api.current_logger().info( - "CLN is not configured on this system; skipping channel switch" + "CLN is not the active package channel; skipping channel switch" ) return try: cln_switch(target=int(get_target_major_version())) except CalledProcessError as e: - # CLOS-4056: Do not inhibit. CLN may be partially configured (legacy - # registration files present but no working registration) on systems - # transitioning to the no-auth scheme, and a failed channel switch - # there is expected — the no-auth repos still serve CL9 packages. + # CLOS-4056: Do not inhibit. Even on systems that ARE using CLN + # as the package channel, a transient CLN-server reachability + # problem at FirstBoot (DNS/network not up yet) shouldn't block + # the upgrade — the no-auth fallback repos still serve packages. reporting.create_report( [ reporting.Title( diff --git a/repos/system_upgrade/cloudlinux/actors/unpinclnmirror/actor.py b/repos/system_upgrade/cloudlinux/actors/unpinclnmirror/actor.py index cc2247d91e..01ddafadf4 100644 --- a/repos/system_upgrade/cloudlinux/actors/unpinclnmirror/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/unpinclnmirror/actor.py @@ -2,7 +2,7 @@ from leapp.actors import Actor from leapp.libraries.common.cllaunch import run_on_cloudlinux -from leapp.libraries.common.cln_detect import is_cln_configured +from leapp.libraries.common.cln_detect import is_cln_package_channel_active from leapp.libraries.common.cln_switch import get_target_userspace_path from leapp.tags import FirstBootPhaseTag, IPUWorkflowTag @@ -20,8 +20,10 @@ class UnpinClnMirror(Actor): @run_on_cloudlinux def process(self): - if not is_cln_configured(): - # CLOS-4056: pinclnmirror was skipped on no-auth systems, nothing to unpin. + if not is_cln_package_channel_active(): + # CLOS-4056: pinclnmirror skipped its work for the same reason + # (CLN is not the package channel here), so there is nothing + # for us to unpin. return target_userspace = get_target_userspace_path() diff --git a/repos/system_upgrade/cloudlinux/libraries/cln_detect.py b/repos/system_upgrade/cloudlinux/libraries/cln_detect.py index 623f36a71a..f5c6532f87 100644 --- a/repos/system_upgrade/cloudlinux/libraries/cln_detect.py +++ b/repos/system_upgrade/cloudlinux/libraries/cln_detect.py @@ -1,18 +1,27 @@ -"""Detection helpers for CLN (CloudLinux Network / Spacewalk) state. +"""Detection helpers for the CloudLinux Network (CLN) package channel. -A system is considered to have CLN *configured* when it has registration -state plus the spacewalk DNF/YUM plugin installed and not explicitly -disabled. Systems that have been migrated to the no-auth (SWNG mirrorlist) -scheme have either: +CLN has historically combined two concerns: - - no `/etc/sysconfig/rhn/systemid` (never registered or deregistered), - - no spacewalk plugin installed (rhn-client-tools >= 3.0.1 removes it), or - - the plugin's `enabled = 0` in its config. + 1. **Registration / identity** — the system is registered with the CLN + server (`/etc/sysconfig/rhn/systemid`, JWT token), used for licensing + and inventory regardless of how packages are delivered. -CLOS-4056: several CloudLinux-specific actors were written when CLN was the -only scheme and assume it is always active. They need to gate their -behavior on `is_cln_configured()` so systems on the no-auth scheme pass -through without bogus inhibitors or crashes. + 2. **Package delivery** — the system pulls CloudLinux packages through + the spacewalk DNF/YUM plugin against the CLN-side channel + (`cloudlinux-x86_64-server-N`). + +The no-auth (SWNG) transition decouples these. New CL8 and CL9 systems +keep CLN **registration** but no longer use CLN as the **package +channel** — packages come from the SWNG mirrorlist via +`/etc/yum.repos.d/cl.repo` (`cl-channel`) instead. `rhn-client-tools +>= 3.0.1` disables the spacewalk plugin to enforce this. + +The CLN-touching actors in this repo only care about the second concern: +they exist to make the CLN package channel work during ELevate. On +systems where the channel has been switched off they should stand down +even though registration may still be present and valid. + +CLOS-4056: gate those actors on `is_cln_package_channel_active()`. """ import os @@ -38,8 +47,20 @@ def _plugin_explicitly_disabled(conf_path): return False -def is_cln_configured(): - """Return True if CLN plumbing is present and not disabled on this system.""" +def is_cln_package_channel_active(): + """Return True when CLN is the active package channel for this system. + + A True result means the spacewalk DNF/YUM plugin is installed, not + explicitly disabled, and the system has CLN registration state for + the plugin to authenticate with. A False result means the system is + either deregistered or has been moved to the no-auth (SWNG) scheme, + so CLN-targeting actions (channel switch, mirror pinning, version + overrides) are not meaningful and should be skipped. + + This is a deliberately heuristic check — it asks "is CLN going to + serve packages here", not "is the system registered with CLN" (the + two were the same thing pre-no-auth and have since diverged). + """ if not os.path.exists(RHN_SYSTEMID): return False @@ -47,7 +68,6 @@ def is_cln_configured(): if not configs: return False - # If any plugin config explicitly disables the plugin, treat as no-auth. for conf in configs: if _plugin_explicitly_disabled(conf): return False diff --git a/repos/system_upgrade/cloudlinux/libraries/tests/test_cln_detect.py b/repos/system_upgrade/cloudlinux/libraries/tests/test_cln_detect.py index ee2bbae411..a048537029 100644 --- a/repos/system_upgrade/cloudlinux/libraries/tests/test_cln_detect.py +++ b/repos/system_upgrade/cloudlinux/libraries/tests/test_cln_detect.py @@ -21,48 +21,49 @@ def _touch(path, content=""): path.write_text(content) -def test_no_systemid_means_no_cln(clean_paths): - # Without /etc/sysconfig/rhn/systemid the system is not registered with CLN. - assert cln_detect.is_cln_configured() is False +def test_no_systemid_means_channel_inactive(clean_paths): + # Without registration the spacewalk plugin can't authenticate, so even + # if the plugin is installed it is not the active package channel. + assert cln_detect.is_cln_package_channel_active() is False -def test_systemid_but_no_plugin_means_no_cln(clean_paths): +def test_systemid_but_no_plugin_means_channel_inactive(clean_paths): _touch(clean_paths["systemid"]) - assert cln_detect.is_cln_configured() is False + assert cln_detect.is_cln_package_channel_active() is False -def test_systemid_and_enabled_dnf_plugin_means_cln(clean_paths): +def test_systemid_and_enabled_dnf_plugin_means_channel_active(clean_paths): _touch(clean_paths["systemid"]) _touch(clean_paths["dnf_conf"], "[main]\nenabled = 1\n") - assert cln_detect.is_cln_configured() is True + assert cln_detect.is_cln_package_channel_active() is True -def test_explicit_disabled_dnf_plugin_means_no_cln(clean_paths): +def test_explicit_disabled_dnf_plugin_means_channel_inactive(clean_paths): _touch(clean_paths["systemid"]) _touch(clean_paths["dnf_conf"], "[main]\nenabled = 0\n") - assert cln_detect.is_cln_configured() is False + assert cln_detect.is_cln_package_channel_active() is False -def test_explicit_disabled_yum_plugin_means_no_cln(clean_paths): +def test_explicit_disabled_yum_plugin_means_channel_inactive(clean_paths): _touch(clean_paths["systemid"]) _touch(clean_paths["yum_conf"], "[main]\nenabled=0\n") - assert cln_detect.is_cln_configured() is False + assert cln_detect.is_cln_package_channel_active() is False -def test_one_plugin_disabled_one_not_means_no_cln(clean_paths): - # If either plugin config disables it, CLN is not usable. +def test_one_plugin_disabled_one_not_means_channel_inactive(clean_paths): + # If either plugin config disables the plugin, treat the channel as off. _touch(clean_paths["systemid"]) _touch(clean_paths["dnf_conf"], "[main]\nenabled = 1\n") _touch(clean_paths["yum_conf"], "[main]\nenabled = 0\n") - assert cln_detect.is_cln_configured() is False + assert cln_detect.is_cln_package_channel_active() is False -def test_plugin_conf_without_enabled_key_means_cln(clean_paths): - # A plugin config that doesn't mention `enabled` defaults to enabled upstream, - # so we must treat it as CLN active. +def test_plugin_conf_without_enabled_key_means_channel_active(clean_paths): + # A plugin config that does not mention `enabled` defaults to enabled + # upstream, so we must treat the channel as active. _touch(clean_paths["systemid"]) _touch(clean_paths["dnf_conf"], "[main]\ntimeout = 120\n") - assert cln_detect.is_cln_configured() is True + assert cln_detect.is_cln_package_channel_active() is True def test_comments_and_blank_lines_ignored(clean_paths): @@ -71,4 +72,4 @@ def test_comments_and_blank_lines_ignored(clean_paths): clean_paths["dnf_conf"], "# some comment\n\n[main]\n# enabled = 0\nenabled = 1\n", ) - assert cln_detect.is_cln_configured() is True + assert cln_detect.is_cln_package_channel_active() is True From 0a31d423b27c4ac8f6443d6a67d4deb0f418ed94 Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Mon, 27 Apr 2026 12:06:10 -0400 Subject: [PATCH 3/7] CLOS-4056: replace em-dashes with ASCII for Python 2.7 / lint compat The previous two commits introduced em-dashes (U+2014) in docstrings and inline comments. The upstream make lint target now greps for any non-ASCII byte (commit 92aee84b) because Python 2.7 source files reject non-ASCII without an encoding declaration, and the leapp framework still supports running on 2.7 in places. Replace each em-dash with an ASCII hyphen so the files lint clean against that gate. --- .../cloudlinux/actors/checkrhnversionoverride/actor.py | 2 +- .../cloudlinux/actors/pinclnmirror/actor.py | 2 +- .../cloudlinux/actors/resetrhnversionoverride/actor.py | 2 +- .../cloudlinux/actors/switchclnchannel/actor.py | 6 +++--- repos/system_upgrade/cloudlinux/libraries/cln_detect.py | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py b/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py index 21a8b3ee13..41cdd8843f 100644 --- a/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/checkrhnversionoverride/actor.py @@ -21,7 +21,7 @@ class CheckRhnVersionOverride(Actor): def process(self): if not is_cln_package_channel_active(): # CLOS-4056: versionOverride only matters when CLN is delivering - # packages — the upgrade rewrites it to drive channel selection. + # packages - the upgrade rewrites it to drive channel selection. # On no-auth (SWNG) systems the package channel is cl-channel, # not CLN, so there is nothing to inspect or warn about even if # /etc/sysconfig/rhn/up2date is still present from registration. diff --git a/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py b/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py index 1bdcb42d9e..478609ce02 100644 --- a/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/pinclnmirror/actor.py @@ -29,7 +29,7 @@ def process(self): if not is_cln_package_channel_active(): # CLOS-4056: pinning the CLN mirror is only meaningful when CLN # is delivering packages. On no-auth (SWNG) systems packages come - # from cl-channel via mirrorlist, so there is nothing to pin — + # from cl-channel via mirrorlist, so there is nothing to pin - # registration may still be in place but is irrelevant here. api.current_logger().info( "CLN is not the active package channel; skipping mirror pinning" diff --git a/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py b/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py index ff446c77a2..408a77f85f 100644 --- a/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/resetrhnversionoverride/actor.py @@ -20,7 +20,7 @@ def process(self): if not is_cln_package_channel_active(): # CLOS-4056: versionOverride is only set/used by the CLN package # channel flow. If the system isn't on CLN for packages, leave - # /etc/sysconfig/rhn/up2date alone — registration metadata there + # /etc/sysconfig/rhn/up2date alone - registration metadata there # is not ours to touch. return diff --git a/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py b/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py index f7c5675c82..4a77da4740 100644 --- a/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/switchclnchannel/actor.py @@ -25,7 +25,7 @@ class SwitchClnChannel(Actor): def process(self): if not is_cln_package_channel_active(): # CLOS-4056: CLN is no longer the package channel here (no-auth / - # SWNG mode). Skipping the channel switch is correct — packages + # SWNG mode). Skipping the channel switch is correct - packages # come from cl-channel / cloudlinux9-baseos instead. The system # may still be CLN-registered for licensing; that is a separate # concern this actor does not need to manage. @@ -40,7 +40,7 @@ def process(self): # CLOS-4056: Do not inhibit. Even on systems that ARE using CLN # as the package channel, a transient CLN-server reachability # problem at FirstBoot (DNS/network not up yet) shouldn't block - # the upgrade — the no-auth fallback repos still serve packages. + # the upgrade - the no-auth fallback repos still serve packages. reporting.create_report( [ reporting.Title( @@ -50,7 +50,7 @@ def process(self): "Command {} failed with exit code {}." " The most probable cause of that is a problem with this system's" " CloudLinux Network registration. If this system now uses the" - " no-auth (SWNG) repository scheme, this failure is harmless —" + " no-auth (SWNG) repository scheme, this failure is harmless -" " CL9 packages come from cl-channel / cloudlinux9-baseos instead" " of CLN.".format(e.command, e.exit_code) ), diff --git a/repos/system_upgrade/cloudlinux/libraries/cln_detect.py b/repos/system_upgrade/cloudlinux/libraries/cln_detect.py index f5c6532f87..8bb4bb2a2b 100644 --- a/repos/system_upgrade/cloudlinux/libraries/cln_detect.py +++ b/repos/system_upgrade/cloudlinux/libraries/cln_detect.py @@ -2,17 +2,17 @@ CLN has historically combined two concerns: - 1. **Registration / identity** — the system is registered with the CLN + 1. **Registration / identity** - the system is registered with the CLN server (`/etc/sysconfig/rhn/systemid`, JWT token), used for licensing and inventory regardless of how packages are delivered. - 2. **Package delivery** — the system pulls CloudLinux packages through + 2. **Package delivery** - the system pulls CloudLinux packages through the spacewalk DNF/YUM plugin against the CLN-side channel (`cloudlinux-x86_64-server-N`). The no-auth (SWNG) transition decouples these. New CL8 and CL9 systems keep CLN **registration** but no longer use CLN as the **package -channel** — packages come from the SWNG mirrorlist via +channel** - packages come from the SWNG mirrorlist via `/etc/yum.repos.d/cl.repo` (`cl-channel`) instead. `rhn-client-tools >= 3.0.1` disables the spacewalk plugin to enforce this. @@ -57,7 +57,7 @@ def is_cln_package_channel_active(): so CLN-targeting actions (channel switch, mirror pinning, version overrides) are not meaningful and should be skipped. - This is a deliberately heuristic check — it asks "is CLN going to + This is a deliberately heuristic check - it asks "is CLN going to serve packages here", not "is the system registered with CLN" (the two were the same thing pre-no-auth and have since diverged). """ From 922b4478c458b8dca346f0c82ee8bcfb05445ba4 Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Wed, 29 Apr 2026 11:01:05 -0400 Subject: [PATCH 4/7] upgrade cli: register --non-interactive alias compatible with leapp 0.18 The earlier patch passed `aliases=['non-interactive']` to @command_opt, which threads through to leapp framework's `add_option`. In 0.18.0 that function does not accept an `aliases` kwarg, so leapp loading failed with `add_option() got an unexpected keyword argument 'aliases'` on every CLI invocation - including `leapp preupgrade` and `leapp upgrade`. argparse natively supports multiple long names per option; we just need to get them past add_option. Add a local `_command_opt_with_aliases` helper that wraps the framework's `_add_opt` directly, so both --nowarn and --non-interactive land on the same argparse argument (dest=nowarn). No framework change required, no consumer changes (args.nowarn keeps working). Verified on a CL8 source VM with leapp-0.18.0-2.el8: `leapp upgrade --help` now prints both names as a single option and parses --non-interactive. Co-Authored-By: Claude Opus 4.7 (1M context) --- commands/upgrade/__init__.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/commands/upgrade/__init__.py b/commands/upgrade/__init__.py index 7528297d82..57b4240ea8 100644 --- a/commands/upgrade/__init__.py +++ b/commands/upgrade/__init__.py @@ -8,7 +8,7 @@ from leapp.exceptions import CommandError, LeappError from leapp.logger import configure_logger from leapp.utils.audit import Execution -from leapp.utils.clicmd import command, command_opt +from leapp.utils.clicmd import command, command_opt, _ensure_command from leapp.utils.output import beautify_actor_exception, report_errors, report_info # NOTE: @@ -16,10 +16,36 @@ # otherwise there might be errors. +def command_opt_with_aliases(name, *aliases, **kwargs): + """Like command_opt, but registers -- AND extra long-form aliases. + + leapp framework's add_option (as of 0.18.0) accepts only one long name, + so a plain `aliases=` kwarg trips on `add_option() got an unexpected + keyword argument 'aliases'`. argparse, however, supports multiple long + forms natively when add_argument is called with several name strings. + We bypass add_option and call the lower-level _add_opt directly. + + `dest` is derived by argparse from the first long form (here `name`), + so existing consumers reading `args.` keep working unchanged. + """ + is_flag = kwargs.pop('is_flag', False) + help_text = kwargs.pop('help', '') + action = kwargs.pop('action', 'store_true' if is_flag else 'store') + inherit = kwargs.pop('inherit', False) + + @_ensure_command + def wrapper(f): + names = ['--' + n.lstrip('-') for n in (name,) + aliases] + f.command._add_opt(*names, action=action, help=help_text, + internal={'wrapped': f, 'inherit': inherit}, + **kwargs) + return f + return wrapper + + @command('upgrade', help='Upgrade the current system to the next available major version.') @command_opt('resume', is_flag=True, help='Continue the last execution after it was stopped (e.g. after reboot)') -@command_opt('nowarn', is_flag=True, help='Do not display interactive warnings', - aliases=['non-interactive']) +@command_opt_with_aliases('nowarn', 'non-interactive', is_flag=True, help='Do not display interactive warnings') @command_opt('reboot', is_flag=True, help='Automatically performs reboot when requested.') @command_opt('whitelist-experimental', action='append', metavar='ActorName', help='Enable experimental actors') @command_opt('debug', is_flag=True, help='Enable debug mode', inherit=False) From 67329c6407bebda055cfde07f2f18bbb04bdead3 Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Wed, 29 Apr 2026 13:29:09 -0400 Subject: [PATCH 5/7] Revert "Rebuild rpmdb before dnf transactions targeting el9 to silence bdb_ro warnings" This reverts commit be4ece2deeb4d4b05e070b555d1ac412f48e02c8. The new call site sat inside _prepare_perform, which mounts an overlayfs at /installroot. rpmdb --rebuilddb does an atomic directory rename to swap the old database with the freshly-converted (bdb -> sqlite) one. Across overlay layers that rename returns EXDEV, so rpm prints error: failed to replace old database with new database! error: replace files in /var/lib/rpm with files from /var/lib/rpmrebuilddb.1 to recover and the dnf_transaction_check actor crashes with exit code 1, blocking every CL8 -> CL9 preupgrade. The two existing _rebuild_rpm_db call sites (perform_transaction_install, install_initramdisk_requirements) use _prepare_transaction with binds=['/:/installroot'], so the rename stays on a single filesystem and works. Those are the load-bearing rebuilds for the actual upgrade. Dropping the new preupgrade-time call only restores the harmless bdb_ro warnings; the upgrade transaction itself is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- repos/system_upgrade/common/libraries/dnfplugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/repos/system_upgrade/common/libraries/dnfplugin.py b/repos/system_upgrade/common/libraries/dnfplugin.py index 0fc011ab61..2d7c3fbd50 100644 --- a/repos/system_upgrade/common/libraries/dnfplugin.py +++ b/repos/system_upgrade/common/libraries/dnfplugin.py @@ -483,8 +483,6 @@ def _prepare_perform(used_repos, target_userspace_info, xfs_info, storage_info, mount_target=os.path.join(context.base_dir, 'installroot'), scratch_reserve=reserve_space) as overlay: with mounting.mount_upgrade_iso_to_root_dir(target_userspace_info.path, target_iso): - if get_target_major_version() == '9': - _rebuild_rpm_db(context, root='/installroot') yield context, overlay, target_repoids From a0a86eea3ad05a6ad7c5410919e737ba51ddf2ff Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Wed, 29 Apr 2026 18:18:15 -0400 Subject: [PATCH 6/7] EnableYumSpacewalkPlugin: rename `config` -> CONFIG_PATH (reserved name) The Actor base class in leapp framework 0.18+ runs self.config = retrieve_config(self.config_schemas) in __init__, which clobbers any class-level `config` attribute the actor defined. Our class-level `config = '/etc/dnf/plugins/spacewalk.conf'` was silently turned into the runtime config dict, so the FirstBoot phase crashed with `TypeError: stat: path should be string, ..., not dict` on every CL upgrade (CLOS-3960 introduced the assignment, but the rename to DEFAULT_CONFIG_PATH in CLOS-3960's library didn't touch the actor's class attribute name). Rename to CONFIG_PATH so the framework leaves it alone. _enable_plugin() already silently no-ops when the file is missing, so no_auth systems (where the spacewalk plugin is removed by rhn-client-tools >= 3.0.1) keep skipping cleanly. Verified live on a CL9 machine: patched actor loads, process() reaches _enable_plugin with the string path, and silently returns False/None when the plugin config is absent. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../cloudlinux/actors/enableyumspacewalkplugin/actor.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/repos/system_upgrade/cloudlinux/actors/enableyumspacewalkplugin/actor.py b/repos/system_upgrade/cloudlinux/actors/enableyumspacewalkplugin/actor.py index 4856c02894..e370791410 100644 --- a/repos/system_upgrade/cloudlinux/actors/enableyumspacewalkplugin/actor.py +++ b/repos/system_upgrade/cloudlinux/actors/enableyumspacewalkplugin/actor.py @@ -11,17 +11,18 @@ class EnableYumSpacewalkPlugin(Actor): consumes = () produces = (Report,) tags = (FirstBootPhaseTag, IPUWorkflowTag) - config = enableyumspacewalkplugin.DEFAULT_CONFIG_PATH + + CONFIG_PATH = enableyumspacewalkplugin.DEFAULT_CONFIG_PATH @run_on_cloudlinux def process(self): _, title = enableyumspacewalkplugin._enable_plugin( - self.config, enableyumspacewalkplugin.ParserClass, self.log + self.CONFIG_PATH, enableyumspacewalkplugin.ParserClass, self.log ) if title: reporting.create_report([ reporting.Title(title), - reporting.Summary("DNF spacewalk plugin must be enabled for CLN channels. Config path: " + self.config), + reporting.Summary("DNF spacewalk plugin must be enabled for CLN channels. Config path: " + self.CONFIG_PATH), reporting.Severity(reporting.Severity.MEDIUM), reporting.Groups([reporting.Groups.SANITY]) ]) From c273d330020ee5f4ddd2dd10c67a69c67ec33686 Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Wed, 29 Apr 2026 19:57:23 -0400 Subject: [PATCH 7/7] Update Governor-related messages and hints in cl-MySQL actor with more details --- .../libraries/clmysql_cloudlinux.py | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/repos/system_upgrade/cloudlinux/actors/clmysqlrepositorysetup/libraries/clmysql_cloudlinux.py b/repos/system_upgrade/cloudlinux/actors/clmysqlrepositorysetup/libraries/clmysql_cloudlinux.py index 840ff5a43c..14a4760609 100644 --- a/repos/system_upgrade/cloudlinux/actors/clmysqlrepositorysetup/libraries/clmysql_cloudlinux.py +++ b/repos/system_upgrade/cloudlinux/actors/clmysqlrepositorysetup/libraries/clmysql_cloudlinux.py @@ -41,7 +41,7 @@ def clmysql_process(lib, repofile_name, repofile_data): reporting.Summary( "MySQL Governor records the installed database type as '{governor}', " "but the mysqld binary on disk belongs to '{rpm}'. " - "This usually means 'mysqlgovernor.py --mysql-version' was run " + "This usually means '/usr/share/lve/dbgovernor/mysqlgovernor.py --mysql-version' was run " "without a follow-up '--install', or packages were changed manually. " "Proceeding could enable the wrong DNF module stream and break the upgrade.".format( governor=detected.governor_type, rpm=detected.pkg_type @@ -56,11 +56,11 @@ def clmysql_process(lib, repofile_name, repofile_data): hint=( "Examine the current state of the system's DB packages." "Complete the pending Governor install:\n" - " mysqlgovernor.py --mysql-version={governor}\n" - " mysqlgovernor.py --install --yes\n" + " /usr/share/lve/dbgovernor/mysqlgovernor.py --mysql-version={governor}\n" + " /usr/share/lve/dbgovernor/mysqlgovernor.py --install --yes\n" "Or reset Governor to match the actual packages:\n" - " mysqlgovernor.py --mysql-version={rpm}\n" - " mysqlgovernor.py --install --yes\n" + " /usr/share/lve/dbgovernor/mysqlgovernor.py --mysql-version={rpm}\n" + " /usr/share/lve/dbgovernor/mysqlgovernor.py --install --yes\n" "Then restart the upgrade process.".format( governor=detected.governor_type, rpm=detected.pkg_type ) @@ -109,7 +109,7 @@ def clmysql_process(lib, repofile_name, repofile_data): "The detected database type is '{}', but the cl-mysql-meta " "repo URL points to '{}'. " "This may happen when the database version was changed " - "without a follow-up 'mysqlgovernor.py --install', or the " + "without a follow-up '/usr/share/lve/dbgovernor/mysqlgovernor.py --install', or the " "cl-mysql.repo file was manually edited. " "Proceeding with the wrong repository would result in " "an incorrect upgrade operation." @@ -125,13 +125,16 @@ def clmysql_process(lib, repofile_name, repofile_data): reporting.Groups([reporting.Groups.INHIBITOR]), reporting.Remediation( hint=( - "Re-run MySQL Governor to regenerate the repository file: " - "mysqlgovernor.py --install --yes, " - "then restart the upgrade process. " - "Alternatively, if the repository file was manually edited, " - "either correct the baseurl to match the installed DB type or " - "set the desired DB type in Governor and re-run --install " - "to have it write the correct URL." + "Download the correct repository file for the installed " + "database type: " + "curl -o /etc/yum.repos.d/cl-mysql.repo " + "http://repo.cloudlinux.com/other/" + "cl${{releasever}}/mysqlmeta/{expected}-common.repo\n" + "Or re-run MySQL Governor to regenerate it " + "(this reinstalls the full DB stack): " + "/usr/share/lve/dbgovernor/mysqlgovernor.py --install --yes\n" + "Then restart the upgrade process." + .format(expected=expected_fragment) ) ), ]