Merge "Update new keywords for GCE quota issue."
diff --git a/Android.bp b/Android.bp
index ec2c2c1..c6e7534 100644
--- a/Android.bp
+++ b/Android.bp
@@ -74,6 +74,7 @@
"acloud_public",
"acloud_restart",
"acloud_setup",
+ "acloud_hostcleanup",
"py-apitools",
"py-dateutil",
"py-google-api-python-client",
@@ -112,6 +113,7 @@
"acloud_proto",
"acloud_restart",
"acloud_setup",
+ "acloud_hostcleanup",
"asuite_cc_client",
"py-apitools",
"py-dateutil",
@@ -232,6 +234,14 @@
}
python_library_host{
+ name: "acloud_hostcleanup",
+ defaults: ["acloud_default"],
+ srcs: [
+ "hostcleanup/*.py",
+ ],
+}
+
+python_library_host{
name: "acloud_metrics",
defaults: ["acloud_default"],
srcs: [
diff --git a/delete/delete.py b/delete/delete.py
index a60f16d..47be404 100644
--- a/delete/delete.py
+++ b/delete/delete.py
@@ -306,7 +306,7 @@
This method can identify the following types of instance names:
local cuttlefish instance: local-instance-<id>
local goldfish instance: local-goldfish-instance-<id>
- remote host goldfish instance: host-<ip_addr>-goldfish-<port>-<build_info>
+ remote host goldfish instance: host-goldfish-<ip_addr>-<port>-<build_info>
remote instance: ins-<uuid>-<build_info>
Args:
diff --git a/delete/delete_test.py b/delete/delete_test.py
index 0359a33..23cb973 100644
--- a/delete/delete_test.py
+++ b/delete/delete_test.py
@@ -163,7 +163,7 @@
cfg_attrs = {"ssh_private_key_path": "cfg_key_path",
"extra_args_ssh_tunnel": "extra args"}
mock_cfg = mock.Mock(spec_set=list(cfg_attrs.keys()), **cfg_attrs)
- instance_name = "host-192.0.2.1-goldfish-5554-123456-sdk_x86_64-sdk"
+ instance_name = "host-goldfish-192.0.2.1-5554-123456-sdk_x86_64-sdk"
delete_report = report.Report(command="delete")
delete.DeleteHostGoldfishInstance(mock_cfg, instance_name,
@@ -212,7 +212,7 @@
mock_reset_lock.assert_called_with("local-instance-2", mock.ANY)
# Test delete remote host instances.
- instances = ["host-192.0.2.1-goldfish-5554-123456-sdk_x86_64-sdk"]
+ instances = ["host-goldfish-192.0.2.1-5554-123456-sdk_x86_64-sdk"]
delete.DeleteInstanceByNames(cfg, instances, "user", "key")
mock_delete_remote_host_gf.assert_called_with(
cfg, instances[0], "user", "key", mock.ANY)
diff --git a/hostcleanup/__init__.py b/hostcleanup/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hostcleanup/__init__.py
diff --git a/hostcleanup/host_cleanup_runner.py b/hostcleanup/host_cleanup_runner.py
new file mode 100644
index 0000000..22b5c14
--- /dev/null
+++ b/hostcleanup/host_cleanup_runner.py
@@ -0,0 +1,125 @@
+# Copyright 2021 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+r"""host cleanup runner
+
+A host cleanup sub task runner will cleanup host to a pristine state.
+"""
+
+from __future__ import print_function
+
+import logging
+import os
+import subprocess
+import textwrap
+
+from acloud.internal import constants
+from acloud.internal.lib import utils
+from acloud.setup import base_task_runner
+from acloud.setup import setup_common
+
+logger = logging.getLogger(__name__)
+
+_PARAGRAPH_BREAK = "="
+_PURGE_PACKAGE_CMD = "sudo apt-get purge --assume-yes %s"
+_UNINSTALL_SUCCESS_MSG = "Package(s) [%s] have uninstalled."
+
+
+class BasePurger(base_task_runner.BaseTaskRunner):
+ """Subtask base runner class for hostcleanup."""
+
+ PURGE_MESSAGE_TITLE = ""
+ PURGE_MESSAGE = ""
+
+ cmds = []
+ purge_packages = []
+
+ def ShouldRun(self):
+ """Check if required packages are all uninstalled.
+
+ Returns:
+ Boolean, True if command list not null.
+ """
+ if not utils.IsSupportedPlatform():
+ return False
+
+ if self.cmds:
+ return True
+
+ utils.PrintColorString(
+ "[%s]: don't have to process." % self.PURGE_MESSAGE_TITLE,
+ utils.TextColors.WARNING)
+ return False
+
+ def _Run(self):
+ """Run purge commands."""
+ utils.PrintColorString("Below commands will be run: \n%s" %
+ "\n".join(self.cmds))
+
+ answer_client = utils.InteractWithQuestion(
+ "\nPress 'y' to continue or anything else to do it myself[y/N]: ",
+ utils.TextColors.WARNING)
+ if answer_client not in constants.USER_ANSWER_YES:
+ return
+
+ for cmd in self.cmds:
+ try:
+ setup_common.CheckCmdOutput(cmd,
+ shell=True,
+ stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as cpe:
+ logger.error("Run command [%s] failed: %s",
+ cmd, cpe.output)
+
+ utils.PrintColorString((_UNINSTALL_SUCCESS_MSG %
+ ",".join(self.purge_packages)),
+ utils.TextColors.OKGREEN)
+
+ def PrintPurgeMessage(self):
+ """Print purge message"""
+ # define the layout of message.
+ console_width = int(os.popen('stty size', 'r').read().split()[1])
+ break_width = int(console_width / 2)
+
+ # start to print purge message.
+ print("\n" + _PARAGRAPH_BREAK * break_width)
+ print(" [%s] " % self.PURGE_MESSAGE_TITLE)
+ print(textwrap.fill(
+ self.PURGE_MESSAGE,
+ break_width - 2,
+ initial_indent=" ",
+ subsequent_indent=" "))
+ print(_PARAGRAPH_BREAK * break_width + "\n")
+
+
+class PackagesUninstaller(BasePurger):
+ """Subtask base runner class for uninstalling packages."""
+
+ PURGE_MESSAGE_TITLE = "Uninstalling packages"
+ PURGE_MESSAGE = ("This will uninstall packages installed previously "
+ "through \"acloud setup --host-setup\"")
+
+ def __init__(self):
+ """Initialize."""
+ packages = []
+ packages.extend(constants.AVD_REQUIRED_PKGS)
+ packages.extend(constants.BASE_REQUIRED_PKGS)
+ packages.append(constants.CUTTLEFISH_COMMOM_PKG)
+
+ self.purge_packages = [pkg for pkg in packages
+ if setup_common.PackageInstalled(pkg)]
+
+ self.cmds = [
+ _PURGE_PACKAGE_CMD % pkg for pkg in self.purge_packages]
+
+ self.PrintPurgeMessage()
diff --git a/hostcleanup/hostcleanup.py b/hostcleanup/hostcleanup.py
new file mode 100644
index 0000000..56b03c9
--- /dev/null
+++ b/hostcleanup/hostcleanup.py
@@ -0,0 +1,31 @@
+# Copyright 2021 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+r"""Hostcleanup entry point.
+
+Hostcleanup will rollback acloud host setup steps.
+"""
+from acloud.hostcleanup import host_cleanup_runner
+
+def Run(args):
+ """Run Hostcleanup.
+
+ Hostcleanup options:
+ -cleanup_pkgs: Uninstall packages.
+
+ Args:
+ args: Namespace object from argparse.parse_args.
+ """
+ # TODO(b/145763747): Need to implement cleanup configs and usergroup.
+ if args.cleanup_pkgs:
+ host_cleanup_runner.PackagesUninstaller().Run()
diff --git a/hostcleanup/hostcleanup_args.py b/hostcleanup/hostcleanup_args.py
new file mode 100644
index 0000000..15c4a92
--- /dev/null
+++ b/hostcleanup/hostcleanup_args.py
@@ -0,0 +1,42 @@
+# Copyright 2021 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+r"""hostcleanup args.
+
+Defines the hostcleanup arg parser.
+"""
+
+CMD_HOSTCLEANUP = "hostcleanup"
+
+
+def GetHostcleanupArgParser(subparser):
+ """Return the hostcleanup arg parser.
+
+ Args:
+ subparser: argparse.ArgumentParser that is attached to main acloud cmd.
+
+ Returns:
+ argparse.ArgumentParser with hostcleanup options defined.
+ """
+ hostcleanup_parser = subparser.add_parser(CMD_HOSTCLEANUP)
+ hostcleanup_parser.required = False
+ hostcleanup_parser.set_defaults(which=CMD_HOSTCLEANUP)
+ hostcleanup_parser.add_argument(
+ "--packages",
+ action="store_true",
+ dest="cleanup_pkgs",
+ required=False,
+ default=True,
+ help="This feature will purge all packages installed by the acloud.")
+
+ return hostcleanup_parser
diff --git a/hostcleanup/hostcleanup_test.py b/hostcleanup/hostcleanup_test.py
new file mode 100644
index 0000000..d24692d
--- /dev/null
+++ b/hostcleanup/hostcleanup_test.py
@@ -0,0 +1,37 @@
+# Copyright 2021 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests for hostcleanup."""
+import unittest
+
+from unittest import mock
+
+from acloud.internal.lib import driver_test_lib
+from acloud.hostcleanup import hostcleanup
+from acloud.hostcleanup import host_cleanup_runner
+
+
+class HostcleanupTest(driver_test_lib.BaseDriverTest):
+ """Test hostcleanup."""
+
+ # pylint: disable=no-self-use
+ @mock.patch.object(host_cleanup_runner, "PackagesUninstaller")
+ def testRun(self, mock_uninstallpkgs):
+ """test Run."""
+ args = mock.MagicMock()
+ hostcleanup.Run(args)
+ mock_uninstallpkgs.assert_called_once()
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/internal/constants.py b/internal/constants.py
index cca3f94..7b24056 100755
--- a/internal/constants.py
+++ b/internal/constants.py
@@ -233,3 +233,10 @@
# The name of download image tool.
FETCH_CVD = "fetch_cvd"
+
+# For setup and cleanup
+# Packages "devscripts" and "equivs" are required for "mk-build-deps".
+AVD_REQUIRED_PKGS = [
+ "devscripts", "equivs", "libvirt-clients", "libvirt-daemon-system"]
+BASE_REQUIRED_PKGS = ["ssvnc", "lzop", "python3-tk"]
+CUTTLEFISH_COMMOM_PKG = "cuttlefish-common"
diff --git a/internal/lib/goldfish_remote_host_client.py b/internal/lib/goldfish_remote_host_client.py
index dd41fb8..08e2f36 100644
--- a/internal/lib/goldfish_remote_host_client.py
+++ b/internal/lib/goldfish_remote_host_client.py
@@ -22,10 +22,10 @@
from acloud.public import config
-_INSTANCE_NAME_FORMAT = ("host-%(ip_addr)s-goldfish-%(console_port)s-"
+_INSTANCE_NAME_FORMAT = ("host-goldfish-%(ip_addr)s-%(console_port)s-"
"%(build_id)s-%(build_target)s")
_INSTANCE_NAME_PATTERN = re.compile(
- r"host-(?P<ip_addr>[\d.]+)-goldfish-(?P<console_port>\d+)-.+")
+ r"host-goldfish-(?P<ip_addr>[\d.]+)-(?P<console_port>\d+)-.+")
# Report keys
_VERSION = "version"
diff --git a/internal/lib/goldfish_remote_host_client_test.py b/internal/lib/goldfish_remote_host_client_test.py
index aec2dbc..3b3c72a 100644
--- a/internal/lib/goldfish_remote_host_client_test.py
+++ b/internal/lib/goldfish_remote_host_client_test.py
@@ -29,7 +29,7 @@
_CONSOLE_PORT = 5554
_BUILD_INFO = {"build_id": "123456",
"build_target": "sdk_phone_x86_64-userdebug"}
- _INSTANCE_NAME = ("host-192.0.2.1-goldfish-5554-"
+ _INSTANCE_NAME = ("host-goldfish-192.0.2.1-5554-"
"123456-sdk_phone_x86_64-userdebug")
_INVALID_NAME = "host-192.0.2.1-123456-aosp_cf_x86_phone-userdebug"
diff --git a/public/acloud_main.py b/public/acloud_main.py
index 8102136..fd094d9 100644
--- a/public/acloud_main.py
+++ b/public/acloud_main.py
@@ -136,6 +136,8 @@
from acloud.restart import restart_args
from acloud.setup import setup
from acloud.setup import setup_args
+from acloud.hostcleanup import hostcleanup
+from acloud.hostcleanup import hostcleanup_args
LOGGING_FMT = "%(asctime)s |%(levelname)s| %(module)s:%(lineno)s| %(message)s"
@@ -177,6 +179,7 @@
reconnect_args.CMD_RECONNECT,
pull_args.CMD_PULL,
restart_args.CMD_RESTART,
+ hostcleanup_args.CMD_HOSTCLEANUP,
])
parser = argparse.ArgumentParser(
description=__doc__,
@@ -260,6 +263,9 @@
# Command "pull"
subparser_list.append(pull_args.GetPullArgParser(subparsers))
+ # Command "hostcleanup"
+ subparser_list.append(hostcleanup_args.GetHostcleanupArgParser(subparsers))
+
# Add common arguments.
for subparser in subparser_list:
acloud_common.AddCommonArguments(subparser)
@@ -476,6 +482,8 @@
reporter = pull.Run(args)
elif args.which == setup_args.CMD_SETUP:
setup.Run(args)
+ elif args.which == hostcleanup_args.CMD_HOSTCLEANUP:
+ hostcleanup.Run(args)
else:
error_msg = "Invalid command %s" % args.which
sys.stderr.write(error_msg)
diff --git a/public/actions/remote_host_gf_device_factory_test.py b/public/actions/remote_host_gf_device_factory_test.py
index 1d848c1..95f3470 100644
--- a/public/actions/remote_host_gf_device_factory_test.py
+++ b/public/actions/remote_host_gf_device_factory_test.py
@@ -38,13 +38,13 @@
constants.BUILD_TARGET: "sdk_x86_64-sdk",
}
_X86_64_INSTANCE_NAME = (
- "host-192.0.2.1-goldfish-5554-123456-sdk_x86_64-sdk")
+ "host-goldfish-192.0.2.1-5554-123456-sdk_x86_64-sdk")
_ARM64_BUILD_INFO = {
constants.BUILD_ID: "123456",
constants.BUILD_TARGET: "sdk_arm64-sdk",
}
_ARM64_INSTANCE_NAME = (
- "host-192.0.2.1-goldfish-5554-123456-sdk_arm64-sdk")
+ "host-goldfish-192.0.2.1-5554-123456-sdk_arm64-sdk")
_CFG_ATTRS = {
"ssh_private_key_path": "cfg_key_path",
"extra_args_ssh_tunnel": "extra args",
diff --git a/setup/host_setup_runner.py b/setup/host_setup_runner.py
index e272d88..c3cbad0 100644
--- a/setup/host_setup_runner.py
+++ b/setup/host_setup_runner.py
@@ -36,11 +36,6 @@
logger = logging.getLogger(__name__)
-# Packages "devscripts" and "equivs" are required for "mk-build-deps".
-_AVD_REQUIRED_PKGS = [
- "devscripts", "equivs", "libvirt-clients", "libvirt-daemon-system"]
-_BASE_REQUIRED_PKGS = ["ssvnc", "lzop", "python3-tk"]
-_CUTTLEFISH_COMMOM_PKG = "cuttlefish-common"
_CF_COMMOM_FOLDER = "cf-common"
_LIST_OF_MODULES = ["kvm_intel", "kvm"]
_UPDATE_APT_GET_CMD = "sudo apt-get update"
@@ -61,9 +56,6 @@
{"mkcert_install_path": _MKCERT_INSTALL_PATH,
"mkcert_url": _MKCERT_URL,
"mkcert_ver": _MKCERT_VERSION})
-_INSTALL_MKCERT_CMD = [
- "sudo apt-get install wget libnss3-tools",
- _MKCERT_DOWNLOAD_CMD]
class BasePkgInstaller(base_task_runner.BaseTaskRunner):
@@ -116,7 +108,7 @@
WELCOME_MESSAGE = ("This step will walk you through the required packages "
"installation for running Android cuttlefish devices "
"on your host.")
- PACKAGES = _AVD_REQUIRED_PKGS
+ PACKAGES = constants.AVD_REQUIRED_PKGS
class HostBasePkgInstaller(BasePkgInstaller):
@@ -125,7 +117,7 @@
WELCOME_MESSAGE_TITLE = "Install base packages on the host"
WELCOME_MESSAGE = ("This step will walk you through the base packages "
"installation for your host.")
- PACKAGES = _BASE_REQUIRED_PKGS
+ PACKAGES = constants.BASE_REQUIRED_PKGS
class CuttlefishCommonPkgInstaller(base_task_runner.BaseTaskRunner):
@@ -146,7 +138,7 @@
# Any required package is not installed or not up-to-date will need to
# run installation task.
- if not setup_common.PackageInstalled(_CUTTLEFISH_COMMOM_PKG):
+ if not setup_common.PackageInstalled(constants.CUTTLEFISH_COMMOM_PKG):
return True
return False
@@ -191,16 +183,14 @@
def _Run(self):
"""Install mkcert packages."""
- cmd = "\n".join(_INSTALL_MKCERT_CMD)
-
if not utils.GetUserAnswerYes("\nStart to install mkcert :\n%s"
"\nEnter 'y' to continue, otherwise N or "
- "enter to exit: " % cmd):
+ "enter to exit: " % _MKCERT_DOWNLOAD_CMD):
sys.exit(constants.EXIT_BY_USER)
if not os.path.isdir(_MKCERT_INSTALL_PATH):
os.mkdir(_MKCERT_INSTALL_PATH)
- setup_common.CheckCmdOutput(cmd, shell=True)
+ setup_common.CheckCmdOutput(_MKCERT_DOWNLOAD_CMD, shell=True)
utils.SetExecutable(os.path.join(_MKCERT_INSTALL_PATH, "mkcert"))
utils.CheckOutput(_MKCERT_CAROOT_CMD, shell=True)
logger.info("Mkcert package is installed at \"%s\" now.",