blob: 9560e774d0fea71bffaa19fb3fe6645108eb6218 [file] [log] [blame]
Sam Chiu81bdc652018-06-29 18:45:08 +08001#!/usr/bin/env python
2#
3# Copyright 2018 - The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16r"""host setup runner
17
18A setup sub task runner to support setting up the local host for AVD local
19instance.
20"""
21
22from __future__ import print_function
23
24import getpass
Sam Chiu81bdc652018-06-29 18:45:08 +080025import logging
herbertxue975b8872020-02-12 14:41:41 +080026import os
27import shutil
chojoyce345c2c02021-08-12 18:24:05 +080028import subprocess
Sam Chiuaa703b62019-10-04 19:47:43 +080029import sys
herbertxue975b8872020-02-12 14:41:41 +080030import tempfile
chojoyce345c2c02021-08-12 18:24:05 +080031import re
Sam Chiu81bdc652018-06-29 18:45:08 +080032
33from acloud.internal import constants
34from acloud.internal.lib import utils
35from acloud.setup import base_task_runner
36from acloud.setup import setup_common
37
herbertxue1512f8a2019-06-27 13:56:23 +080038
Sam Chiu81bdc652018-06-29 18:45:08 +080039logger = logging.getLogger(__name__)
40
herbertxue975b8872020-02-12 14:41:41 +080041# Packages "devscripts" and "equivs" are required for "mk-build-deps".
42_AVD_REQUIRED_PKGS = [
43 "devscripts", "equivs", "libvirt-clients", "libvirt-daemon-system"]
CY Lan7faf3b52020-06-16 03:12:58 +000044_BASE_REQUIRED_PKGS = ["ssvnc", "lzop", "python3-tk"]
herbertxue975b8872020-02-12 14:41:41 +080045_CUTTLEFISH_COMMOM_PKG = "cuttlefish-common"
46_CF_COMMOM_FOLDER = "cf-common"
chojoyce345c2c02021-08-12 18:24:05 +080047_CHECK_KVM_CMD = "virt-host-validate"
48_CHECK_KVM_PATTERN = r".*:.Checking for hardware virtualization.*PASS\n"
49
Sam Chiu81bdc652018-06-29 18:45:08 +080050_LIST_OF_MODULES = ["kvm_intel", "kvm"]
Kevin Chengf756bbd2018-10-11 13:50:00 -070051_UPDATE_APT_GET_CMD = "sudo apt-get update"
herbertxue975b8872020-02-12 14:41:41 +080052_INSTALL_CUTTLEFISH_COMMOM_CMD = [
53 "git clone https://github.com/google/android-cuttlefish.git {git_folder}",
54 "cd {git_folder}",
55 "yes | sudo mk-build-deps -i -r -B",
56 "dpkg-buildpackage -uc -us",
57 "sudo apt-get install -y -f ../cuttlefish-common_*_amd64.deb"]
chojoyce3c5ad5c2021-07-05 10:44:14 +080058_MKCERT_URL = "https://github.com/FiloSottile/mkcert"
59_MKCERT_VERSION = "v1.4.3"
60_MKCERT_INSTALL_PATH = os.path.join(os.path.expanduser("~"), ".config",
61 constants.TOOL_NAME, "mkcert")
62_MKCERT_CAROOT_CMD = "%s/mkcert -install" % _MKCERT_INSTALL_PATH
63_MKCERT_DOWNLOAD_CMD = ("wget -O %(mkcert_install_path)s/mkcert "
64 "%(mkcert_url)s/releases/download/"
65 "%(mkcert_ver)s/mkcert-%(mkcert_ver)s-linux-amd64" %
66 {"mkcert_install_path": _MKCERT_INSTALL_PATH,
67 "mkcert_url": _MKCERT_URL,
68 "mkcert_ver": _MKCERT_VERSION})
69_INSTALL_MKCERT_CMD = [
70 "sudo apt-get install wget libnss3-tools",
71 _MKCERT_DOWNLOAD_CMD]
Sam Chiu81bdc652018-06-29 18:45:08 +080072
73
Kevin Chengeb997272019-06-05 14:53:18 -070074class BasePkgInstaller(base_task_runner.BaseTaskRunner):
75 """Subtask base runner class for installing packages."""
Sam Chiu81bdc652018-06-29 18:45:08 +080076
Kevin Chengeb997272019-06-05 14:53:18 -070077 # List of packages for child classes to override.
78 PACKAGES = []
Sam Chiu81bdc652018-06-29 18:45:08 +080079
80 def ShouldRun(self):
81 """Check if required packages are all installed.
82
83 Returns:
84 Boolean, True if required packages are not installed.
85 """
Sam Chiu6c738d62018-12-04 10:29:02 +080086 if not utils.IsSupportedPlatform():
Sam Chiu81bdc652018-06-29 18:45:08 +080087 return False
88
89 # Any required package is not installed or not up-to-date will need to
90 # run installation task.
Kevin Chengeb997272019-06-05 14:53:18 -070091 for pkg_name in self.PACKAGES:
Sam Chiu81bdc652018-06-29 18:45:08 +080092 if not setup_common.PackageInstalled(pkg_name):
93 return True
94
95 return False
96
97 def _Run(self):
Kevin Chengeb997272019-06-05 14:53:18 -070098 """Install specified packages."""
Sam Chiuaa703b62019-10-04 19:47:43 +080099 cmd = "\n".join(
100 [setup_common.PKG_INSTALL_CMD % pkg
101 for pkg in self.PACKAGES
102 if not setup_common.PackageInstalled(pkg)])
Sam Chiu81bdc652018-06-29 18:45:08 +0800103
Sam Chiuaa703b62019-10-04 19:47:43 +0800104 if not utils.GetUserAnswerYes("\nStart to install package(s):\n%s"
herbertxue97af4a62020-11-05 17:12:56 +0800105 "\nEnter 'y' to continue, otherwise N or "
106 "enter to exit: " % cmd):
Sam Chiuaa703b62019-10-04 19:47:43 +0800107 sys.exit(constants.EXIT_BY_USER)
Sam Chiu81bdc652018-06-29 18:45:08 +0800108
Kevin Chengf756bbd2018-10-11 13:50:00 -0700109 setup_common.CheckCmdOutput(_UPDATE_APT_GET_CMD, shell=True)
Kevin Chengeb997272019-06-05 14:53:18 -0700110 for pkg in self.PACKAGES:
Sam Chiu81bdc652018-06-29 18:45:08 +0800111 setup_common.InstallPackage(pkg)
112
Kevin Chengeb997272019-06-05 14:53:18 -0700113 logger.info("All package(s) installed now.")
114
115
116class AvdPkgInstaller(BasePkgInstaller):
117 """Subtask runner class for installing packages for local instances."""
118
119 WELCOME_MESSAGE_TITLE = ("Install required packages for host setup for "
120 "local instances")
121 WELCOME_MESSAGE = ("This step will walk you through the required packages "
122 "installation for running Android cuttlefish devices "
123 "on your host.")
124 PACKAGES = _AVD_REQUIRED_PKGS
125
126
127class HostBasePkgInstaller(BasePkgInstaller):
128 """Subtask runner class for installing base host packages."""
129
130 WELCOME_MESSAGE_TITLE = "Install base packages on the host"
131 WELCOME_MESSAGE = ("This step will walk you through the base packages "
132 "installation for your host.")
133 PACKAGES = _BASE_REQUIRED_PKGS
Sam Chiu81bdc652018-06-29 18:45:08 +0800134
135
herbertxue975b8872020-02-12 14:41:41 +0800136class CuttlefishCommonPkgInstaller(base_task_runner.BaseTaskRunner):
137 """Subtask base runner class for installing cuttlefish-common."""
138
139 WELCOME_MESSAGE_TITLE = "Install cuttlefish-common packages on the host"
140 WELCOME_MESSAGE = ("This step will walk you through the cuttlefish-common "
141 "packages installation for your host.")
142
143 def ShouldRun(self):
144 """Check if cuttlefish-common package is installed.
145
146 Returns:
147 Boolean, True if cuttlefish-common is not installed.
148 """
149 if not utils.IsSupportedPlatform():
150 return False
151
152 # Any required package is not installed or not up-to-date will need to
153 # run installation task.
154 if not setup_common.PackageInstalled(_CUTTLEFISH_COMMOM_PKG):
155 return True
156 return False
157
158 def _Run(self):
159 """Install cuttlefilsh-common packages."""
160 cf_common_path = os.path.join(tempfile.mkdtemp(), _CF_COMMOM_FOLDER)
161 logger.debug("cuttlefish-common path: %s", cf_common_path)
162 cmd = "\n".join(sub_cmd.format(git_folder=cf_common_path)
163 for sub_cmd in _INSTALL_CUTTLEFISH_COMMOM_CMD)
164
165 if not utils.GetUserAnswerYes("\nStart to install cuttlefish-common :\n%s"
herbertxue97af4a62020-11-05 17:12:56 +0800166 "\nEnter 'y' to continue, otherwise N or "
167 "enter to exit: " % cmd):
herbertxue975b8872020-02-12 14:41:41 +0800168 sys.exit(constants.EXIT_BY_USER)
169 try:
170 setup_common.CheckCmdOutput(cmd, shell=True)
171 finally:
172 shutil.rmtree(os.path.dirname(cf_common_path))
173 logger.info("Cuttlefish-common package installed now.")
174
chojoyce3c5ad5c2021-07-05 10:44:14 +0800175class MkcertPkgInstaller(base_task_runner.BaseTaskRunner):
176 """Subtask base runner class for installing mkcert."""
177
178 WELCOME_MESSAGE_TITLE = "Install mkcert package on the host"
179 WELCOME_MESSAGE = ("This step will walk you through the mkcert "
180 "package installation to your host for "
181 "assuring a secure localhost url connection "
182 "when launching an AVD over webrtc")
183
184 def ShouldRun(self):
185 """Check if mkcert package is installed.
186
187 Returns:
188 Boolean, True if mkcert is not installed.
189 """
190 if not utils.IsSupportedPlatform():
191 return False
192
193 if not os.path.exists(os.path.join(_MKCERT_INSTALL_PATH, "mkcert")):
194 return True
195 return False
196
197 def _Run(self):
198 """Install mkcert packages."""
199 cmd = "\n".join(_INSTALL_MKCERT_CMD)
200
201 if not utils.GetUserAnswerYes("\nStart to install mkcert :\n%s"
202 "\nEnter 'y' to continue, otherwise N or "
203 "enter to exit: " % cmd):
204 sys.exit(constants.EXIT_BY_USER)
205
chojoyce49e7cb92021-08-04 12:01:32 +0800206 if not os.path.isdir(_MKCERT_INSTALL_PATH):
207 os.mkdir(_MKCERT_INSTALL_PATH)
chojoyce3c5ad5c2021-07-05 10:44:14 +0800208 setup_common.CheckCmdOutput(cmd, shell=True)
209 utils.SetExecutable(os.path.join(_MKCERT_INSTALL_PATH, "mkcert"))
210 utils.CheckOutput(_MKCERT_CAROOT_CMD, shell=True)
211 logger.info("Mkcert package is installed at \"%s\" now.",
212 _MKCERT_INSTALL_PATH)
herbertxue975b8872020-02-12 14:41:41 +0800213
Sam Chiu81bdc652018-06-29 18:45:08 +0800214class CuttlefishHostSetup(base_task_runner.BaseTaskRunner):
215 """Subtask class that setup host for cuttlefish."""
216
217 WELCOME_MESSAGE_TITLE = "Host Enviornment Setup"
218 WELCOME_MESSAGE = (
219 "This step will help you to setup enviornment for running Android "
220 "cuttlefish devices on your host. That includes adding user to kvm "
221 "related groups and checking required linux modules."
222 )
223
224 def ShouldRun(self):
225 """Check host user groups and modules.
226
227 Returns:
228 Boolean: False if user is in all required groups and all modules
229 are reloaded.
230 """
Sam Chiu6c738d62018-12-04 10:29:02 +0800231 if not utils.IsSupportedPlatform():
Sam Chiu81bdc652018-06-29 18:45:08 +0800232 return False
233
herbertxue07293a32018-11-05 20:40:11 +0800234 return not (utils.CheckUserInGroups(constants.LIST_CF_USER_GROUPS)
Sam Chiu81bdc652018-06-29 18:45:08 +0800235 and self._CheckLoadedModules(_LIST_OF_MODULES))
236
237 @staticmethod
Sam Chiu81bdc652018-06-29 18:45:08 +0800238 def _CheckLoadedModules(module_list):
239 """Check if the modules are all in use.
240
241 Args:
242 module_list: The list of module name.
243 Returns:
244 True if all modules are in use.
245 """
246 logger.info("Checking if modules are loaded: %s", module_list)
247 lsmod_output = setup_common.CheckCmdOutput("lsmod", print_cmd=False)
248 current_modules = [r.split()[0] for r in lsmod_output.splitlines()]
249 all_modules_present = True
250 for module in module_list:
251 if module not in current_modules:
252 logger.info("missing module: %s", module)
253 all_modules_present = False
254 return all_modules_present
255
chojoyce345c2c02021-08-12 18:24:05 +0800256 @staticmethod
257 def _IsSupportedKvm():
258 """Check if support kvm.
259
260 Returns:
261 True if environment supported kvm.
262 """
263 process = subprocess.Popen([_CHECK_KVM_CMD],
264 stdout=subprocess.PIPE,
265 universal_newlines=True)
266 stdout, _ = process.communicate()
267 re_pattern = re.compile(_CHECK_KVM_PATTERN)
268 if re_pattern.match(stdout):
269 return True
270
271 utils.PrintColorString(
272 "The environment doesn't support virtualization. If you want to "
273 "launch AVD on the local instance, Please refer to http://go/"
274 "acloud-cloudtop#acloud-create-local-instance-on-the-cloudtop ",
275 utils.TextColors.FAIL)
276 return False
277
Sam Chiu81bdc652018-06-29 18:45:08 +0800278 def _Run(self):
279 """Setup host environment for local cuttlefish instance support."""
chojoyce345c2c02021-08-12 18:24:05 +0800280 if not self._IsSupportedKvm():
281 return
282
Sam Chiu81bdc652018-06-29 18:45:08 +0800283 # TODO: provide --uid args to let user use prefered username
284 username = getpass.getuser()
285 setup_cmds = [
286 "sudo rmmod kvm_intel",
287 "sudo rmmod kvm",
288 "sudo modprobe kvm",
289 "sudo modprobe kvm_intel"]
Sam Chiuafbc6582018-09-04 20:47:13 +0800290 for group in constants.LIST_CF_USER_GROUPS:
Sam Chiu81bdc652018-06-29 18:45:08 +0800291 setup_cmds.append("sudo usermod -aG %s % s" % (group, username))
292
293 print("Below commands will be run:")
294 for setup_cmd in setup_cmds:
295 print(setup_cmd)
296
297 if self._ConfirmContinue():
298 for setup_cmd in setup_cmds:
299 setup_common.CheckCmdOutput(setup_cmd, shell=True)
300 print("Host environment setup has done!")
301
302 @staticmethod
303 def _ConfirmContinue():
304 """Ask user if they want to continue.
305
306 Returns:
307 True if user answer yes.
308 """
309 answer_client = utils.InteractWithQuestion(
herbertxue97af4a62020-11-05 17:12:56 +0800310 "\nEnter 'y' to continue, otherwise N or enter to exit: ",
Sam Chiu81bdc652018-06-29 18:45:08 +0800311 utils.TextColors.WARNING)
312 return answer_client in constants.USER_ANSWER_YES