%PDF- %PDF-
Direktori : /lib/python3/dist-packages/uaclient/cli/ |
Current File : //lib/python3/dist-packages/uaclient/cli/disable.py |
import json import logging import textwrap from typing import Dict, List # noqa: F401 from uaclient import ( api, config, contract, entitlements, event_logger, exceptions, messages, status, util, ) from uaclient.api.u.pro.services.dependencies.v1 import ( ServiceWithDependencies, _dependencies, ) from uaclient.api.u.pro.status.enabled_services.v1 import _enabled_services from uaclient.cli import cli_util, constants from uaclient.entitlements.entitlement_status import CanDisableFailure LOG = logging.getLogger(util.replace_top_level_logger_name(__name__)) def prompt_for_dependency_handling( cfg: config.UAConfig, service: str, all_dependencies: List[ServiceWithDependencies], enabled_service_names: List[str], called_name: str, service_title: str, ): dependent_services = [] for s in all_dependencies: if s.name == service or s.name not in enabled_service_names: continue for requirement in s.depends_on: if requirement.name == service: dependent_services.append(s.name) for dependent_service in dependent_services: dependent_service_title = entitlements.get_title( cfg, dependent_service ) user_msg = messages.DEPENDENT_SERVICE.format( service_being_disabled=service_title, dependent_service=dependent_service_title, ) if not util.prompt_for_confirmation(msg=user_msg): raise exceptions.DependentServiceStopsDisable( service_being_disabled=service_title, dependent_service=dependent_service_title, ) @cli_util.verify_json_format_args @cli_util.assert_root @cli_util.assert_attached(cli_util._raise_enable_disable_unattached_error) @cli_util.assert_lock_file("pro disable") def action_disable(args, *, cfg, **kwargs): """Perform the disable action on a list of entitlements. @return: 0 on success, 1 otherwise """ processed_services = [] failed_services = [] errors = [] warnings = [] # type: List[Dict[str, str]] json_response = { "_schema_version": event_logger.JSON_SCHEMA_VERSION, "result": "success", "needs_reboot": False, } json_output = args.format == "json" # HACK NOTICE: interactive_only_print here will be a no-op "null_print" # function defined above if args.format == "json". We use this function # throughout enable for things that should get printed in the normal # interactive output so that they don't get printed for the json output. interactive_only_print = cli_util.create_interactive_only_print_function( json_output ) if args.purge and args.assume_yes: raise exceptions.InvalidOptionCombination( option1="--purge", option2="--assume-yes" ) names = getattr(args, "service", []) ( entitlements_found, entitlements_not_found, ) = entitlements.get_valid_entitlement_names(names, cfg) enabled_service_names = [ s.name for s in _enabled_services(cfg).enabled_services ] all_dependencies = _dependencies(cfg).services ret = True for ent_name in entitlements_found: ent_cls = entitlements.entitlement_factory(cfg=cfg, name=ent_name) ent = ent_cls(cfg, assume_yes=args.assume_yes, purge=args.purge) variant = ent.enabled_variant if variant is not None: ent = variant if not args.assume_yes: # this never happens for json output because we assert earlier that # assume_yes must be True for json output try: prompt_for_dependency_handling( cfg, ent.name, all_dependencies, enabled_service_names, called_name=ent_name, service_title=ent.title, ) except exceptions.UbuntuProError as e: LOG.exception(e) interactive_only_print(e.msg) interactive_only_print( messages.ENABLE_FAILED.format(title=ent.title) ) ret = False continue if json_output: progress = api.ProgressWrapper() else: progress = api.ProgressWrapper(cli_util.CLIEnableDisableProgress()) progress.total_steps = ent.calculate_total_disable_steps() try: disable_ret, reason = ent.disable(progress) status.status(cfg=cfg) # Update the status cache if not disable_ret: ret = False failed_services.append(ent_name) if reason is not None and isinstance( reason, CanDisableFailure ): if reason.message is not None: interactive_only_print(reason.message.msg) errors.append( { "type": "service", "service": ent.name, "message": reason.message.msg, "message_code": reason.message.name, } ) else: processed_services.append(ent_name) ent_reboot_required = ent._check_for_reboot() if ent_reboot_required: json_response["needs_reboot"] = True interactive_only_print( messages.ENABLE_REBOOT_REQUIRED_TMPL.format( operation="disable operation" ) ) except exceptions.UbuntuProError as e: ret = False failed_services.append(ent_name) interactive_only_print(e.msg) interactive_only_print( messages.DISABLE_FAILED_TMPL.format(title=ent.title) ) errors.append( { "type": "service", "service": ent.name, "message": e.msg, "message_code": e.msg_code, "additional_info": e.additional_info, } ) if entitlements_not_found: ret = False valid_names = ( "Try " + ", ".join(entitlements.valid_services(cfg=cfg, allow_beta=True)) + "." ) service_msg = "\n".join( textwrap.wrap( valid_names, width=80, break_long_words=False, break_on_hyphens=False, ) ) err = exceptions.InvalidServiceOpError( operation="disable", invalid_service=", ".join(entitlements_not_found), service_msg=service_msg, ) interactive_only_print(err.msg) errors.append( { "type": "system", "service": None, "message": err.msg, "message_code": err.msg_code, "additional_info": err.additional_info, } ) contract_client = contract.UAContractClient(cfg) contract_client.update_activity_token() if json_output: processed_services.sort() failed_services.sort() json_response["result"] = "success" if ret else "failure" json_response["processed_services"] = processed_services json_response["failed_services"] = failed_services json_response["errors"] = errors json_response["warnings"] = warnings print( json.dumps( json_response, cls=util.DatetimeAwareJSONEncoder, sort_keys=True, ) ) return 0 if ret else 1 def add_parser(subparsers, cfg: config.UAConfig): """Build or extend an arg parser for disable subcommand.""" parser = subparsers.add_parser("disable", help=messages.CLI_ROOT_DISABLE) parser.set_defaults(action=action_disable) usage = constants.USAGE_TMPL.format( name=constants.NAME, command="disable <service> [<service>]" ) parser.description = messages.CLI_DISABLE_DESC parser.usage = usage parser.prog = "disable" parser._positionals.title = messages.CLI_ARGS parser._optionals.title = messages.CLI_FLAGS parser.add_argument( "service", action="store", nargs="+", help=( messages.CLI_DISABLE_SERVICE.format( options=", ".join(entitlements.valid_services(cfg=cfg)) ) ), ) parser.add_argument( "--assume-yes", action="store_true", help=messages.CLI_ASSUME_YES.format(command="disable"), ) parser.add_argument( "--format", action="store", choices=["cli", "json"], default="cli", help=messages.CLI_FORMAT_DESC.format(default="cli"), ) parser.add_argument( "--purge", action="store_true", help=messages.CLI_PURGE, ) return parser