From d9204e3492f68e031ab658dfc59f370879d31e93 Mon Sep 17 00:00:00 2001 From: Joe VLcek Date: Tue, 6 Aug 2024 16:32:41 -0400 Subject: [PATCH] feat: Eliminate command 'remove' from subscription-manager This PR eliminates the subscription-manager remove sub-command. fixes: https://issues.redhat.com/browse/CCT-426 --- etc-conf/subscription-manager.completion.sh | 26 +-- man/subscription-manager.8 | 32 --- .../cli_command/remove.py | 212 ------------------ src/subscription_manager/managercli.py | 2 - test/cli_command/test_remove.py | 37 --- test/test_remove.py | 87 ------- 6 files changed, 1 insertion(+), 395 deletions(-) delete mode 100644 src/subscription_manager/cli_command/remove.py delete mode 100644 test/cli_command/test_remove.py delete mode 100644 test/test_remove.py diff --git a/etc-conf/subscription-manager.completion.sh b/etc-conf/subscription-manager.completion.sh index 258a0524ed..75e9eee9b2 100644 --- a/etc-conf/subscription-manager.completion.sh +++ b/etc-conf/subscription-manager.completion.sh @@ -60,26 +60,6 @@ _subscription_manager_unregister() COMPREPLY=($(compgen -W "${opts}" -- ${1})) } -_subscription_manager_remove() -{ - # try to autocomplete serial number as well - case $prev in - --serial) - SERIALS=$(LANG=C /usr/sbin/subscription-manager list --consumed 2>/dev/null | sed -ne "s|Serial:\s*\(\S*\)|\1|p" ) - COMPREPLY=($(compgen -W "${SERIALS}" -- ${1})) - return 0 - ;; - --pool) - POOLS=$(LANG=C /usr/sbin/subscription-manager list --consumed 2>/dev/null | sed -ne "s|Pool ID:\s*(\S*\)|\1|p" ) - COMPREPLY=($(compgen -W "${POOLS}" -- ${1})) - return 0 - ;; - esac - local opts="--serial --pool --all - ${_subscription_manager_common_opts}" - COMPREPLY=($(compgen -W "${opts}" -- ${1})) -} - _subscription_manager_clean() { local opts="-h --help" @@ -227,7 +207,7 @@ _subscription_manager() # top-level commands and options opts="auto-attach clean config environments facts identity list orgs - repo-override plugins refresh register release remove repos status + repo-override plugins refresh register release repos status syspurpose unregister version ${_subscription_manager_help_opts}" case "${first}" in @@ -250,10 +230,6 @@ _subscription_manager() "_subscription_manager_$first" "${cur}" "${prev}" return 0 ;; - remove) - "_subscription_manager_remove" "${cur}" "${prev}" - return 0 - ;; auto-attach) "_subscription_manager_auto_attach" "${cur}" "${prev}" return 0 diff --git a/man/subscription-manager.8 b/man/subscription-manager.8 index c4d9c429b6..7f1397e569 100644 --- a/man/subscription-manager.8 +++ b/man/subscription-manager.8 @@ -79,9 +79,6 @@ must be passed as system arguments in a non-interactive session. .IP 4. auto-attach -.IP -5. remove - .IP 6. release @@ -299,33 +296,6 @@ Disables the auto-attach option for the system. If auto-attach is disabled, then .B --show Shows whether auto-attach is enabled on the systems. -.SS REMOVE OPTIONS -The -.B remove -command removes a subscription from the system. (This does not uninstall the associated products.) - -.TP -.B --serial=SERIALNUMBER -Gives the serial number of the subscription certificate for the specific product to remove from the system. Subscription certificates attached to a system are in a certificate, in -.B /etc/pki/entitlement/.pem. -To remove multiple subscriptions, use the -.B --serial -option multiple times. - -.TP -.B --pool=POOLID -Removes all subscription certificates for the specified pool id from the system. -To remove multiple sets of subscriptions, use the -.B --pool -option multiple times. - -.TP -.B --all -Removes -.I all -of the subscriptions attached to a system. - - .SS RELEASE OPTIONS The .B release @@ -903,8 +873,6 @@ Overall Status: Insufficient As the structures of subscription configuration have changed, some of the original management commands have become obsolete. These commands have been replaced with updated commands. .TP -.B unsubscribe -This has been replaced with \fBremove\fP. .SH USAGE .B subscription-manager diff --git a/src/subscription_manager/cli_command/remove.py b/src/subscription_manager/cli_command/remove.py deleted file mode 100644 index 3e58b1dd48..0000000000 --- a/src/subscription_manager/cli_command/remove.py +++ /dev/null @@ -1,212 +0,0 @@ -# -# Subscription manager command line utility. -# -# Copyright (c) 2021 Red Hat, Inc. -# -# This software is licensed to you under the GNU General Public License, -# version 2 (GPLv2). There is NO WARRANTY for this software, express or -# implied, including the implied warranties of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 -# along with this software; if not, see -# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. -# -# Red Hat trademarks are not licensed under GPLv2. No permission is -# granted to use or replicate Red Hat trademarks that are incorporated -# in this software or its documentation. -# -import logging -import os - -import rhsm.connection as connection - -from rhsmlib.services import entitlement - -from subscription_manager.cli import system_exit -from subscription_manager.cli_command.cli import CliCommand, handle_exception -from subscription_manager.i18n import ungettext, ugettext as _ -from subscription_manager.utils import unique_list_items - -log = logging.getLogger(__name__) - - -class RemoveCommand(CliCommand): - def __init__(self): - super(RemoveCommand, self).__init__(self._command_name(), self._short_description(), self._primary()) - - self.parser.add_argument( - "--serial", - action="append", - dest="serials", - metavar="SERIAL", - help=_("certificate serial number to remove (can be specified more than once)"), - ) - self.parser.add_argument( - "--pool", - action="append", - dest="pool_ids", - metavar="POOL_ID", - help=_("the ID of the pool to remove (can be specified more than once)"), - ) - self.parser.add_argument( - "--all", - dest="all", - action="store_true", - help=_("remove all subscriptions from this system"), - ) - - def _short_description(self): - return _("Remove all or specific subscriptions from this system") - - def _command_name(self): - return "remove" - - def _primary(self): - return True - - def _validate_options(self): - if self.options.serials: - bad = False - for serial in self.options.serials: - if not serial.isdigit(): - print(_("Error: '{serial}' is not a valid serial number").format(serial=serial)) - bad = True - if bad: - system_exit(os.EX_USAGE) - elif self.options.pool_ids: - if not self.cp.has_capability("remove_by_pool_id"): - system_exit( - os.EX_UNAVAILABLE, - _( - "Error: The registered entitlement server does not support remove --pool." - "\nInstead, use the remove --serial option." - ), - ) - elif not self.options.all and not self.options.pool_ids: - system_exit( - os.EX_USAGE, - _("Error: This command requires that you specify one of --serial, --pool or --all."), - ) - - def _print_unbind_ids_result(self, success, failure, id_name): - if success: - if id_name == "pools": - print(_("The entitlement server successfully removed these pools:")) - elif id_name == "serial numbers": - print(_("The entitlement server successfully removed these serial numbers:")) - else: - print(_("The entitlement server successfully removed these IDs:")) - for id_ in success: - print(" {id_}".format(id_=id_)) - if failure: - if id_name == "pools": - print(_("The entitlement server failed to remove these pools:")) - elif id_name == "serial numbers": - print(_("The entitlement server failed to remove these serial numbers:")) - else: - print(_("The entitlement server failed to remove these IDs:")) - for id_ in failure: - print(" {id_}".format(id_=id_)) - - def _do_command(self): - """ - Executes the command. - """ - self._validate_options() - return_code = 0 - if self.is_registered(): - ent_service = entitlement.EntitlementService(self.cp) - try: - if self.options.all: - total = ent_service.remove_all_entitlements() - # total will be None on older Candlepins that don't - # support returning the number of subscriptions unsubscribed from - if total is None: - print(_("All subscriptions have been removed at the server.")) - else: - count = total["deletedRecords"] - print( - ungettext( - "%s subscription removed at the server.", - "%s subscriptions removed at the server.", - count, - ) - % count - ) - else: - # Try to remove subscriptions defined by pool IDs first (remove --pool=...) - if self.options.pool_ids: - ( - removed_pools, - unremoved_pools, - removed_serials, - ) = ent_service.remove_entitlements_by_pool_ids(self.options.pool_ids) - if not removed_pools: - return_code = 1 - self._print_unbind_ids_result(removed_pools, unremoved_pools, "pools") - else: - removed_serials = [] - # Then try to remove subscriptions defined by serials (remove --serial=...) - unremoved_serials = [] - if self.options.serials: - serials = unique_list_items(self.options.serials) - # Don't remove serials already removed by a pool - serials_to_remove = [serial for serial in serials if serial not in removed_serials] - _removed_serials, unremoved_serials = ent_service.remove_entitlements_by_serials( - serials_to_remove - ) - removed_serials.extend(_removed_serials) - if not _removed_serials: - return_code = 1 - # Print final result of removing pools - self._print_unbind_ids_result(removed_serials, unremoved_serials, "serial numbers") - except connection.GoneException as ge: - raise ge - except connection.RestlibException as err: - log.error(err) - - system_exit(os.EX_SOFTWARE, err) - except Exception as e: - handle_exception( - _("Unable to perform remove due to the following exception: {e}").format(e=e), e - ) - else: - # We never got registered, just remove the cert - try: - if self.options.all: - total = 0 - for ent in self.entitlement_dir.list(): - ent.delete() - total = total + 1 - print( - ungettext( - "{total} subscription removed from this system.", - "{total} subscriptions removed from this system.", - total, - ).format(total=total) - ) - else: - if self.options.serials or self.options.pool_ids: - serials = self.options.serials or [] - pool_ids = self.options.pool_ids or [] - count = 0 - for ent in self.entitlement_dir.list(): - ent_pool_id = str(getattr(ent.pool, "id", None) or "") - if str(ent.serial) in serials or ent_pool_id in pool_ids: - ent.delete() - print( - _( - "Subscription with serial number {serial} removed from this system" - ).format(serial=str(ent.serial)) - ) - count = count + 1 - if count == 0: - return_code = 1 - except Exception as e: - handle_exception( - _("Unable to perform remove due to the following exception: {e}").format(e=e), e - ) - - # it is okay to call this no matter what happens above, - # it's just a notification to perform a check - self._request_validity_check() - return return_code diff --git a/src/subscription_manager/managercli.py b/src/subscription_manager/managercli.py index 027e984540..4a930f9250 100644 --- a/src/subscription_manager/managercli.py +++ b/src/subscription_manager/managercli.py @@ -36,7 +36,6 @@ from subscription_manager.cli_command.refresh import RefreshCommand from subscription_manager.cli_command.register import RegisterCommand from subscription_manager.cli_command.release import ReleaseCommand -from subscription_manager.cli_command.remove import RemoveCommand from subscription_manager.cli_command.repos import ReposCommand from subscription_manager.cli_command.status import StatusCommand from subscription_manager.cli_command.syspurpose import SyspurposeCommand @@ -64,7 +63,6 @@ def __init__(self): StatusCommand, EnvironmentsCommand, VersionCommand, - RemoveCommand, PluginsCommand, AutohealCommand, OverrideCommand, diff --git a/test/cli_command/test_remove.py b/test/cli_command/test_remove.py deleted file mode 100644 index d39a5cd21b..0000000000 --- a/test/cli_command/test_remove.py +++ /dev/null @@ -1,37 +0,0 @@ -import os - -from ..test_managercli import TestCliProxyCommand -from subscription_manager import managercli - - -class TestRemoveCommand(TestCliProxyCommand): - command_class = managercli.RemoveCommand - - def test_validate_serial(self): - self.cc.main(["--serial", "12345"]) - self.cc._validate_options() - - def test_validate_serial_not_numbers(self): - self.cc.main(["--serial", "this is not a number"]) - try: - self.cc._validate_options() - except SystemExit as e: - self.assertEqual(e.code, os.EX_USAGE) - - def test_serial_no_value(self): - try: - self.cc.main(["--serial"]) - except SystemExit as e: - self.assertEqual(e.code, 2) - - def test_validate_access_to_remove_by_pool(self): - self.cc.main(["--pool", "a2ee88488bbd32ed8edfa2"]) - self.cc.cp._capabilities = ["remove_by_pool_id"] - self.cc._validate_options() - - def test_validate_no_access_to_remove_by_pool(self): - self.cc.main(["--pool", "a2ee88488bbd32ed8edfa2"]) - try: - self.cc._validate_options() - except SystemExit as e: - self.assertEqual(e.code, 69) diff --git a/test/test_remove.py b/test/test_remove.py deleted file mode 100644 index 38224e2f17..0000000000 --- a/test/test_remove.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) 2012 Red Hat, Inc. -# -# This software is licensed to you under the GNU General Public License, -# version 2 (GPLv2). There is NO WARRANTY for this software, express or -# implied, including the implied warranties of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 -# along with this software; if not, see -# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. -# -# Red Hat trademarks are not licensed under GPLv2. No permission is -# granted to use or replicate Red Hat trademarks that are incorporated -# in this software or its documentation. -# - -from subscription_manager import managercli -from subscription_manager import injection as inj - -from .stubs import ( - StubEntitlementDirectory, - StubProductDirectory, - StubEntActionInvoker, - StubEntitlementCertificate, - StubProduct, - StubPool, -) -from . import fixture - - -# This is a copy of CliUnSubscribeTests for the new name. -class CliRemoveTests(fixture.SubManFixture): - def test_unsubscribe_registered(self): - cmd = managercli.RemoveCommand() - - mock_identity = self._inject_mock_valid_consumer() - managercli.EntCertActionInvoker = StubEntActionInvoker - - cmd.main(["--all"]) - self.assertEqual(cmd.cp.called_unbind_uuid, mock_identity.uuid) - - serial1 = "123456" - cmd.main(["--serial=%s" % serial1]) - self.assertEqual(cmd.cp.called_unbind_serial, [serial1]) - cmd.cp.reset() - - serial2 = "789012" - cmd.main(["--serial=%s" % serial1, "--serial=%s" % serial2]) - self.assertEqual(cmd.cp.called_unbind_serial, [serial1, serial2]) - cmd.cp.reset() - - pool_id1 = "39993922b" - cmd.main( - ["--serial=%s" % serial1, "--serial=%s" % serial2, "--pool=%s" % pool_id1, "--pool=%s" % pool_id1] - ) - self.assertEqual(cmd.cp.called_unbind_serial, [serial1, serial2]) - self.assertEqual(cmd.cp.called_unbind_pool_id, [pool_id1]) - - def test_unsubscribe_unregistered(self): - prod = StubProduct("stub_product") - ent = StubEntitlementCertificate(prod) - - inj.provide(inj.ENT_DIR, StubEntitlementDirectory([ent])) - inj.provide(inj.PROD_DIR, StubProductDirectory([])) - cmd = managercli.RemoveCommand() - - self._inject_mock_invalid_consumer() - - cmd.main(["--all"]) - self.assertTrue(cmd.entitlement_dir.list_called) - self.assertTrue(ent.is_deleted) - - prod = StubProduct("stub_product") - pool = StubPool("stub_pool") - ent1 = StubEntitlementCertificate(prod) - ent2 = StubEntitlementCertificate(prod) - ent3 = StubEntitlementCertificate(prod) - ent4 = StubEntitlementCertificate(prod, pool=pool) - - inj.provide(inj.ENT_DIR, StubEntitlementDirectory([ent1, ent2, ent3, ent4])) - inj.provide(inj.PROD_DIR, StubProductDirectory([])) - cmd = managercli.RemoveCommand() - - cmd.main(["--serial=%s" % ent1.serial, "--serial=%s" % ent3.serial, "--pool=%s" % ent4.pool.id]) - self.assertTrue(cmd.entitlement_dir.list_called) - self.assertTrue(ent1.is_deleted) - self.assertFalse(ent2.is_deleted) - self.assertTrue(ent3.is_deleted) - self.assertTrue(ent4.is_deleted)