1# Copyright 2021 - The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14r"""host cleanup runner 15 16A host cleanup sub task runner will cleanup host to a pristine state. 17""" 18 19from __future__ import print_function 20 21import logging 22import os 23import subprocess 24import textwrap 25 26from acloud.internal import constants 27from acloud.internal.lib import utils 28from acloud.setup import base_task_runner 29from acloud.setup import setup_common 30 31logger = logging.getLogger(__name__) 32 33_PARAGRAPH_BREAK = "=" 34_PURGE_PACKAGE_CMD = "sudo apt-get purge --assume-yes %s" 35_UNINSTALL_SUCCESS_MSG = "Package(s) [%s] have uninstalled." 36 37 38class BasePurger(base_task_runner.BaseTaskRunner): 39 """Subtask base runner class for hostcleanup.""" 40 41 PURGE_MESSAGE_TITLE = "" 42 PURGE_MESSAGE = "" 43 44 cmds = [] 45 purge_packages = [] 46 47 def ShouldRun(self): 48 """Check if required packages are all uninstalled. 49 50 Returns: 51 Boolean, True if command list not null. 52 """ 53 if not utils.IsSupportedPlatform(): 54 return False 55 56 if self.cmds: 57 return True 58 59 utils.PrintColorString( 60 "[%s]: don't have to process." % self.PURGE_MESSAGE_TITLE, 61 utils.TextColors.WARNING) 62 return False 63 64 def _Run(self): 65 """Run purge commands.""" 66 utils.PrintColorString("Below commands will be run: \n%s" % 67 "\n".join(self.cmds)) 68 69 answer_client = utils.InteractWithQuestion( 70 "\nPress 'y' to continue or anything else to do it myself[y/N]: ", 71 utils.TextColors.WARNING) 72 if answer_client not in constants.USER_ANSWER_YES: 73 return 74 75 for cmd in self.cmds: 76 try: 77 setup_common.CheckCmdOutput(cmd, 78 shell=True, 79 stderr=subprocess.STDOUT) 80 except subprocess.CalledProcessError as cpe: 81 logger.error("Run command [%s] failed: %s", 82 cmd, cpe.output) 83 84 utils.PrintColorString((_UNINSTALL_SUCCESS_MSG % 85 ",".join(self.purge_packages)), 86 utils.TextColors.OKGREEN) 87 88 def PrintPurgeMessage(self): 89 """Print purge message""" 90 # define the layout of message. 91 console_width = int(os.popen('stty size', 'r').read().split()[1]) 92 break_width = int(console_width / 2) 93 94 # start to print purge message. 95 print("\n" + _PARAGRAPH_BREAK * break_width) 96 print(" [%s] " % self.PURGE_MESSAGE_TITLE) 97 print(textwrap.fill( 98 self.PURGE_MESSAGE, 99 break_width - 2, 100 initial_indent=" ", 101 subsequent_indent=" ")) 102 print(_PARAGRAPH_BREAK * break_width + "\n") 103 104 105class PackagesUninstaller(BasePurger): 106 """Subtask base runner class for uninstalling packages.""" 107 108 PURGE_MESSAGE_TITLE = "Uninstalling packages" 109 PURGE_MESSAGE = ("This will uninstall packages installed previously " 110 "through \"acloud setup --host-setup\"") 111 112 def __init__(self): 113 """Initialize.""" 114 packages = [] 115 packages.extend(constants.AVD_REQUIRED_PKGS) 116 packages.extend(constants.BASE_REQUIRED_PKGS) 117 packages.append(constants.CUTTLEFISH_COMMOM_PKG) 118 119 self.purge_packages = [pkg for pkg in packages 120 if setup_common.PackageInstalled(pkg)] 121 122 self.cmds = [ 123 _PURGE_PACKAGE_CMD % pkg for pkg in self.purge_packages] 124 125 self.PrintPurgeMessage() 126