blob: 395cfe0dd73ecca871cbbf03b5f0b720f68c8f2e [file] [log] [blame]
Kevin Cheng3ce4b452018-08-23 14:47:22 -07001#!/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"""LocalImageLocalInstance class.
17
18Create class that is responsible for creating a local instance AVD with a
19local image.
20"""
21
Sam Chiuafbc6582018-09-04 20:47:13 +080022from __future__ import print_function
23import logging
24import os
25import subprocess
herbertxue61007092018-10-10 11:56:57 +080026import sys
Sam Chiuafbc6582018-09-04 20:47:13 +080027
28from acloud import errors
Kevin Cheng3ce4b452018-08-23 14:47:22 -070029from acloud.create import base_avd_create
Sam Chiuafbc6582018-09-04 20:47:13 +080030from acloud.create import create_common
31from acloud.internal import constants
32from acloud.internal.lib import utils
Kevin Chengae7a49d2018-10-18 14:11:22 -070033from acloud.public import report
Sam Chiuafbc6582018-09-04 20:47:13 +080034from acloud.setup import host_setup_runner
35
36logger = logging.getLogger(__name__)
37
38_BOOT_COMPLETE = "VIRTUAL_DEVICE_BOOT_COMPLETED"
Sam Chiuafbc6582018-09-04 20:47:13 +080039_CMD_LAUNCH_CVD_ARGS = (" --daemon --cpus %s --x_res %s --y_res %s --dpi %s "
40 "--memory_mb %s --blank_data_image_mb %s "
41 "--data_policy always_create "
42 "--system_image_dir %s "
Kevin Chengc992c342018-11-06 00:45:53 -080043 "--vnc_server_port %s")
Sam Chiuafbc6582018-09-04 20:47:13 +080044_CMD_PGREP = "pgrep"
45_CMD_SG = "sg "
46_CMD_STOP_CVD = "stop_cvd"
Kevin Cheng2aa41da2018-10-11 15:00:48 -070047_CONFIRM_RELAUNCH = ("\nCuttlefish AVD is already running. \n"
Kevin Chengae7a49d2018-10-18 14:11:22 -070048 "Enter 'y' to terminate current instance and launch a new "
49 "instance, enter anything else to exit out [y]: ")
Sam Chiuafbc6582018-09-04 20:47:13 +080050_ENV_ANDROID_HOST_OUT = "ANDROID_HOST_OUT"
Kevin Cheng3ce4b452018-08-23 14:47:22 -070051
52
53class LocalImageLocalInstance(base_avd_create.BaseAVDCreate):
54 """Create class for a local image local instance AVD."""
55
Kevin Chengae7a49d2018-10-18 14:11:22 -070056 @utils.TimeExecute(function_description="Total time: ",
57 print_before_call=False, print_status=False)
58 def _CreateAVD(self, avd_spec):
Kevin Cheng3ce4b452018-08-23 14:47:22 -070059 """Create the AVD.
60
61 Args:
62 avd_spec: AVDSpec object that tells us what we're going to create.
63 """
Sam Chiuafbc6582018-09-04 20:47:13 +080064 local_image_path, launch_cvd_path = self.GetImageArtifactsPath(avd_spec)
65
66 cmd = self.PrepareLaunchCVDCmd(launch_cvd_path,
67 avd_spec.hw_property,
Kevin Chengc992c342018-11-06 00:45:53 -080068 local_image_path)
Sam Chiuafbc6582018-09-04 20:47:13 +080069 try:
herbertxue61007092018-10-10 11:56:57 +080070 self.CheckLaunchCVD(cmd, os.path.dirname(launch_cvd_path))
Sam Chiuafbc6582018-09-04 20:47:13 +080071 except errors.LaunchCVDFail as launch_error:
72 raise launch_error
73
Kevin Chengae7a49d2018-10-18 14:11:22 -070074 result_report = report.Report("local")
75 result_report.SetStatus(report.Status.SUCCESS)
76 result_report.AddData(key="devices",
Sam Chiu7a477f52018-10-22 11:20:36 +080077 value={"adb_port": constants.DEFAULT_ADB_PORT,
78 constants.VNC_PORT: constants.DEFAULT_VNC_PORT})
79 # Launch vnc client if we're auto-connecting.
80 if avd_spec.autoconnect:
81 utils.LaunchVNCFromReport(result_report, avd_spec)
Kevin Chengae7a49d2018-10-18 14:11:22 -070082 return result_report
Sam Chiuafbc6582018-09-04 20:47:13 +080083
84 @staticmethod
85 def GetImageArtifactsPath(avd_spec):
86 """Get image artifacts path.
87
88 This method will check if local image and launch_cvd are exist and
89 return the tuple path where they are located respectively.
90 For remote image, RemoteImageLocalInstance will override this method and
91 return the artifacts path which is extracted and downloaded from remote.
92
93 Args:
94 avd_spec: AVDSpec object that tells us what we're going to create.
95
96 Returns:
97 Tuple of (local image file, launch_cvd package) paths.
98 """
99 try:
100 # Check if local image is exist.
101 create_common.VerifyLocalImageArtifactsExist(
102 avd_spec.local_image_dir)
103
104 # TODO(b/117306227): help user to build out images and host package if
105 # anything needed is not found.
106 except errors.GetLocalImageError as imgerror:
107 logger.error(imgerror.message)
108 raise imgerror
109
110 # Check if launch_cvd is exist.
111 launch_cvd_path = os.path.join(
herbertxue61007092018-10-10 11:56:57 +0800112 os.environ.get(_ENV_ANDROID_HOST_OUT), "bin", constants.CMD_LAUNCH_CVD)
Sam Chiuafbc6582018-09-04 20:47:13 +0800113 if not os.path.exists(launch_cvd_path):
114 raise errors.GetCvdLocalHostPackageError(
115 "No launch_cvd found. Please run \"m launch_cvd\" first")
116
117 return avd_spec.local_image_dir, launch_cvd_path
118
119 @staticmethod
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700120 def _AddUserGroupsToCmd(cmd):
121 """Add the user groups to the command if necessary.
Sam Chiuafbc6582018-09-04 20:47:13 +0800122
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700123 As part of local host setup to enable local instance support,
124 the user is added to certain groups. For those settings to
125 take effect systemwide requires the user to log out and
126 log back in. In the scenario where the user has run setup and
127 hasn't logged out, we still want them to be able to launch a
128 local instance so add the user to the groups as part of the
129 command to ensure success.
130
131 The reason using here-doc instead of '&' is all operations need to be
132 ran in ths same pid. Here's an example cmd:
Sam Chiuafbc6582018-09-04 20:47:13 +0800133 $ sg kvm << EOF
134 sg libvirt
135 sg cvdnetwork
136 launch_cvd --cpus 2 --x_res 1280 --y_res 720 --dpi 160 --memory_mb 4096
137 EOF
138
139 Args:
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700140 cmd: String of the command to prepend the user groups to.
141
142 Returns:
143 String of the command with the user groups prepended to it if
144 necessary, otherwise the same existing command.
145 """
146 user_group_cmd = ""
147 host_setup = host_setup_runner.CuttlefishHostSetup()
148 if not host_setup.CheckUserInGroups(constants.LIST_CF_USER_GROUPS):
149 logger.debug("Need to add user groups to the command")
150 for idx, group in enumerate(constants.LIST_CF_USER_GROUPS):
151 user_group_cmd += _CMD_SG + group
152 if idx == 0:
153 user_group_cmd += " <<EOF\n"
154 else:
155 user_group_cmd += "\n"
156 cmd += "\nEOF"
157 user_group_cmd += cmd
158 logger.debug("user group cmd: %s", user_group_cmd)
159 return user_group_cmd
160
161 def PrepareLaunchCVDCmd(self, launch_cvd_path, hw_property,
Kevin Chengc992c342018-11-06 00:45:53 -0800162 system_image_dir):
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700163 """Prepare launch_cvd command.
164
165 Create the launch_cvd commands with all the required args and add
166 in the user groups to it if necessary.
167
168 Args:
Sam Chiuafbc6582018-09-04 20:47:13 +0800169 launch_cvd_path: String of launch_cvd path.
170 hw_property: dict object of hw property.
171 system_image_dir: String of local images path.
Sam Chiuafbc6582018-09-04 20:47:13 +0800172
173 Returns:
174 String, launch_cvd cmd.
175 """
176 launch_cvd_w_args = launch_cvd_path + _CMD_LAUNCH_CVD_ARGS % (
177 hw_property["cpu"], hw_property["x_res"], hw_property["y_res"],
178 hw_property["dpi"], hw_property["memory"], hw_property["disk"],
Kevin Chengc992c342018-11-06 00:45:53 -0800179 system_image_dir, constants.DEFAULT_VNC_PORT)
Sam Chiuafbc6582018-09-04 20:47:13 +0800180
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700181 launch_cmd = self._AddUserGroupsToCmd(launch_cvd_w_args)
182 logger.debug("launch_cvd cmd:\n %s", launch_cmd)
183 return launch_cmd
Sam Chiuafbc6582018-09-04 20:47:13 +0800184
herbertxue61007092018-10-10 11:56:57 +0800185 @utils.TimeExecute(function_description="Waiting for AVD(s) to boot up")
186 def CheckLaunchCVD(self, cmd, host_pack_dir):
Sam Chiuafbc6582018-09-04 20:47:13 +0800187 """Execute launch_cvd command and wait for boot up completed.
188
189 Args:
190 cmd: String, launch_cvd command.
herbertxue61007092018-10-10 11:56:57 +0800191 host_pack_dir: String of host package directory.
Sam Chiuafbc6582018-09-04 20:47:13 +0800192 """
Sam Chiuafbc6582018-09-04 20:47:13 +0800193 # Cuttlefish support launch single AVD at one time currently.
194 if self._IsLaunchCVDInUse():
195 logger.info("Cuttlefish AVD is already running.")
196 if utils.GetUserAnswerYes(_CONFIRM_RELAUNCH):
herbertxue61007092018-10-10 11:56:57 +0800197 stop_cvd_cmd = os.path.join(host_pack_dir, _CMD_STOP_CVD)
198 with open(os.devnull, "w") as dev_null:
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700199 subprocess.check_call(
200 self._AddUserGroupsToCmd(stop_cvd_cmd),
201 stderr=dev_null, stdout=dev_null, shell=True)
herbertxue61007092018-10-10 11:56:57 +0800202 else:
203 print("Exiting out")
204 sys.exit()
Sam Chiuafbc6582018-09-04 20:47:13 +0800205
Sam Chiuf6fc9fd2018-11-06 15:55:34 +0800206 try:
207 # Check the result of launch_cvd command.
208 # An exit code of 0 is equivalent to VIRTUAL_DEVICE_BOOT_COMPLETED
209 logger.debug(subprocess.check_output(cmd, shell=True,
210 stderr=subprocess.STDOUT))
211 except subprocess.CalledProcessError as error:
Sam Chiuafbc6582018-09-04 20:47:13 +0800212 raise errors.LaunchCVDFail(
Sam Chiuf6fc9fd2018-11-06 15:55:34 +0800213 "Can't launch cuttlefish AVD.%s. \nFor more detail: "
214 "~/cuttlefish_runtime/launcher.log" % error.message)
Sam Chiuafbc6582018-09-04 20:47:13 +0800215
216 @staticmethod
217 def _IsLaunchCVDInUse():
218 """Check if launch_cvd is running.
219
220 Returns:
221 Boolean, True if launch_cvd is running. False otherwise.
222 """
223 try:
herbertxue61007092018-10-10 11:56:57 +0800224 with open(os.devnull, "w") as dev_null:
225 subprocess.check_call([_CMD_PGREP, constants.CMD_LAUNCH_CVD],
226 stderr=dev_null, stdout=dev_null)
Sam Chiuafbc6582018-09-04 20:47:13 +0800227 return True
228 except subprocess.CalledProcessError:
229 # launch_cvd process is not in use.
230 return False