MINI Sh3ll
from typing import List
from uaclient import (
daemon,
entitlements,
exceptions,
lock,
messages,
timer,
util,
)
from uaclient.api import ProgressWrapper
from uaclient.api.api import APIEndpoint
from uaclient.api.data_types import AdditionalInfo, ErrorWarningObject
from uaclient.api.u.pro.security.status.reboot_required.v1 import (
_reboot_required,
)
from uaclient.api.u.pro.status.is_attached.v1 import _is_attached
from uaclient.config import UAConfig
from uaclient.data_types import (
BoolDataValue,
DataObject,
Field,
StringDataValue,
data_list,
)
from uaclient.files import state_files
from uaclient.timer.update_messaging import update_motd_messages
class DetachResult(DataObject, AdditionalInfo):
fields = [
Field("disabled", data_list(StringDataValue)),
Field("reboot_required", BoolDataValue),
]
def __init__(self, disabled: List[str], reboot_required: bool):
self.disabled = disabled
self.reboot_required = reboot_required
def detach() -> DetachResult:
return _detach(UAConfig())
def _detach(cfg: UAConfig) -> DetachResult:
if not util.we_are_currently_root():
raise exceptions.NonRootUserError
try:
with lock.RetryLock(
lock_holder="pro.api.u.pro.detach.v1",
):
ret = _detach_in_lock(cfg)
except Exception as e:
lock.clear_lock_file_if_present()
raise e
return ret
def _detach_in_lock(cfg: UAConfig) -> DetachResult:
if not _is_attached(cfg).is_attached:
return DetachResult(
disabled=[],
reboot_required=False,
)
disabled = []
warnings = [] # type: List[ErrorWarningObject]
for ent_name in entitlements.entitlements_disable_order(cfg):
try:
ent_cls = entitlements.entitlement_factory(cfg=cfg, name=ent_name)
except exceptions.EntitlementNotFoundError:
continue
ent = ent_cls(cfg=cfg, assume_yes=True)
# For detach, we should not consider that a service
# cannot be disabled because of dependent services,
# since we are going to disable all of them anyway
can_disable, _ = ent.can_disable(ignore_dependent_services=True)
if can_disable:
ret, reason = ent.disable(ProgressWrapper())
if not ret:
if reason and reason.message:
msg = reason.message.msg
code = reason.message.name
else:
msg = messages.DISABLE_FAILED_TMPL.format(title=ent_name)
code = ""
warnings.append(
ErrorWarningObject(
title=msg,
code=code,
meta={"service": ent_name},
)
)
else:
disabled.append(ent_name)
state_files.delete_state_files()
cfg.machine_token_file.delete()
update_motd_messages(cfg)
daemon.start()
timer.stop()
reboot_required_result = _reboot_required(cfg)
result = DetachResult(
disabled=sorted(disabled),
reboot_required=reboot_required_result.reboot_required == "yes",
)
result.warnings = warnings
return result
endpoint = APIEndpoint(
version="v1",
name="Detach",
fn=_detach,
options_cls=None,
)
OHA YOOOO