blob: df9d03ef0c4365ebbaf2cdd66d985a2b0c5d2f77 [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# TODO(b/117366819): Currently --serial_number is not working.
40_CMD_LAUNCH_CVD_ARGS = (" --daemon --cpus %s --x_res %s --y_res %s --dpi %s "
41 "--memory_mb %s --blank_data_image_mb %s "
42 "--data_policy always_create "
43 "--system_image_dir %s "
44 "--vnc_server_port %s "
45 "--serial_number %s")
46_CMD_PGREP = "pgrep"
47_CMD_SG = "sg "
48_CMD_STOP_CVD = "stop_cvd"
Kevin Cheng2aa41da2018-10-11 15:00:48 -070049_CONFIRM_RELAUNCH = ("\nCuttlefish AVD is already running. \n"
Kevin Chengae7a49d2018-10-18 14:11:22 -070050 "Enter 'y' to terminate current instance and launch a new "
51 "instance, enter anything else to exit out [y]: ")
Sam Chiuafbc6582018-09-04 20:47:13 +080052_CVD_SERIAL_PREFIX = "acloudCF"
53_ENV_ANDROID_HOST_OUT = "ANDROID_HOST_OUT"
Kevin Cheng3ce4b452018-08-23 14:47:22 -070054
55
56class LocalImageLocalInstance(base_avd_create.BaseAVDCreate):
57 """Create class for a local image local instance AVD."""
58
Kevin Chengae7a49d2018-10-18 14:11:22 -070059 @utils.TimeExecute(function_description="Total time: ",
60 print_before_call=False, print_status=False)
61 def _CreateAVD(self, avd_spec):
Kevin Cheng3ce4b452018-08-23 14:47:22 -070062 """Create the AVD.
63
64 Args:
65 avd_spec: AVDSpec object that tells us what we're going to create.
66 """
Sam Chiuafbc6582018-09-04 20:47:13 +080067 local_image_path, launch_cvd_path = self.GetImageArtifactsPath(avd_spec)
68
69 cmd = self.PrepareLaunchCVDCmd(launch_cvd_path,
70 avd_spec.hw_property,
71 local_image_path,
72 avd_spec.flavor)
73 try:
herbertxue61007092018-10-10 11:56:57 +080074 self.CheckLaunchCVD(cmd, os.path.dirname(launch_cvd_path))
Sam Chiuafbc6582018-09-04 20:47:13 +080075 except errors.LaunchCVDFail as launch_error:
76 raise launch_error
77
Sam Chiuafbc6582018-09-04 20:47:13 +080078 if avd_spec.autoconnect:
Kevin Chengae7a49d2018-10-18 14:11:22 -070079 utils.LaunchVncClient()
Sam Chiuafbc6582018-09-04 20:47:13 +080080
Kevin Chengae7a49d2018-10-18 14:11:22 -070081 # TODO(b/117366819): Should return the correct device serial according
82 # to --serial_number.
83 result_report = report.Report("local")
84 result_report.SetStatus(report.Status.SUCCESS)
85 result_report.AddData(key="devices",
86 value={"adb_port": constants.DEFAULT_ADB_PORT})
87 return result_report
Sam Chiuafbc6582018-09-04 20:47:13 +080088
89 @staticmethod
90 def GetImageArtifactsPath(avd_spec):
91 """Get image artifacts path.
92
93 This method will check if local image and launch_cvd are exist and
94 return the tuple path where they are located respectively.
95 For remote image, RemoteImageLocalInstance will override this method and
96 return the artifacts path which is extracted and downloaded from remote.
97
98 Args:
99 avd_spec: AVDSpec object that tells us what we're going to create.
100
101 Returns:
102 Tuple of (local image file, launch_cvd package) paths.
103 """
104 try:
105 # Check if local image is exist.
106 create_common.VerifyLocalImageArtifactsExist(
107 avd_spec.local_image_dir)
108
109 # TODO(b/117306227): help user to build out images and host package if
110 # anything needed is not found.
111 except errors.GetLocalImageError as imgerror:
112 logger.error(imgerror.message)
113 raise imgerror
114
115 # Check if launch_cvd is exist.
116 launch_cvd_path = os.path.join(
herbertxue61007092018-10-10 11:56:57 +0800117 os.environ.get(_ENV_ANDROID_HOST_OUT), "bin", constants.CMD_LAUNCH_CVD)
Sam Chiuafbc6582018-09-04 20:47:13 +0800118 if not os.path.exists(launch_cvd_path):
119 raise errors.GetCvdLocalHostPackageError(
120 "No launch_cvd found. Please run \"m launch_cvd\" first")
121
122 return avd_spec.local_image_dir, launch_cvd_path
123
124 @staticmethod
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700125 def _AddUserGroupsToCmd(cmd):
126 """Add the user groups to the command if necessary.
Sam Chiuafbc6582018-09-04 20:47:13 +0800127
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700128 As part of local host setup to enable local instance support,
129 the user is added to certain groups. For those settings to
130 take effect systemwide requires the user to log out and
131 log back in. In the scenario where the user has run setup and
132 hasn't logged out, we still want them to be able to launch a
133 local instance so add the user to the groups as part of the
134 command to ensure success.
135
136 The reason using here-doc instead of '&' is all operations need to be
137 ran in ths same pid. Here's an example cmd:
Sam Chiuafbc6582018-09-04 20:47:13 +0800138 $ sg kvm << EOF
139 sg libvirt
140 sg cvdnetwork
141 launch_cvd --cpus 2 --x_res 1280 --y_res 720 --dpi 160 --memory_mb 4096
142 EOF
143
144 Args:
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700145 cmd: String of the command to prepend the user groups to.
146
147 Returns:
148 String of the command with the user groups prepended to it if
149 necessary, otherwise the same existing command.
150 """
151 user_group_cmd = ""
152 host_setup = host_setup_runner.CuttlefishHostSetup()
153 if not host_setup.CheckUserInGroups(constants.LIST_CF_USER_GROUPS):
154 logger.debug("Need to add user groups to the command")
155 for idx, group in enumerate(constants.LIST_CF_USER_GROUPS):
156 user_group_cmd += _CMD_SG + group
157 if idx == 0:
158 user_group_cmd += " <<EOF\n"
159 else:
160 user_group_cmd += "\n"
161 cmd += "\nEOF"
162 user_group_cmd += cmd
163 logger.debug("user group cmd: %s", user_group_cmd)
164 return user_group_cmd
165
166 def PrepareLaunchCVDCmd(self, launch_cvd_path, hw_property,
167 system_image_dir, flavor):
168 """Prepare launch_cvd command.
169
170 Create the launch_cvd commands with all the required args and add
171 in the user groups to it if necessary.
172
173 Args:
Sam Chiuafbc6582018-09-04 20:47:13 +0800174 launch_cvd_path: String of launch_cvd path.
175 hw_property: dict object of hw property.
176 system_image_dir: String of local images path.
177 flavor: String of flavor type.
178
179 Returns:
180 String, launch_cvd cmd.
181 """
182 launch_cvd_w_args = launch_cvd_path + _CMD_LAUNCH_CVD_ARGS % (
183 hw_property["cpu"], hw_property["x_res"], hw_property["y_res"],
184 hw_property["dpi"], hw_property["memory"], hw_property["disk"],
cylan66713722018-10-06 01:38:26 +0800185 system_image_dir, constants.DEFAULT_VNC_PORT,
186 _CVD_SERIAL_PREFIX+flavor)
Sam Chiuafbc6582018-09-04 20:47:13 +0800187
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700188 launch_cmd = self._AddUserGroupsToCmd(launch_cvd_w_args)
189 logger.debug("launch_cvd cmd:\n %s", launch_cmd)
190 return launch_cmd
Sam Chiuafbc6582018-09-04 20:47:13 +0800191
herbertxue61007092018-10-10 11:56:57 +0800192 @utils.TimeExecute(function_description="Waiting for AVD(s) to boot up")
193 def CheckLaunchCVD(self, cmd, host_pack_dir):
Sam Chiuafbc6582018-09-04 20:47:13 +0800194 """Execute launch_cvd command and wait for boot up completed.
195
196 Args:
197 cmd: String, launch_cvd command.
herbertxue61007092018-10-10 11:56:57 +0800198 host_pack_dir: String of host package directory.
Sam Chiuafbc6582018-09-04 20:47:13 +0800199 """
Sam Chiuafbc6582018-09-04 20:47:13 +0800200 # Cuttlefish support launch single AVD at one time currently.
201 if self._IsLaunchCVDInUse():
202 logger.info("Cuttlefish AVD is already running.")
203 if utils.GetUserAnswerYes(_CONFIRM_RELAUNCH):
herbertxue61007092018-10-10 11:56:57 +0800204 stop_cvd_cmd = os.path.join(host_pack_dir, _CMD_STOP_CVD)
205 with open(os.devnull, "w") as dev_null:
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700206 subprocess.check_call(
207 self._AddUserGroupsToCmd(stop_cvd_cmd),
208 stderr=dev_null, stdout=dev_null, shell=True)
herbertxue61007092018-10-10 11:56:57 +0800209 else:
210 print("Exiting out")
211 sys.exit()
Sam Chiuafbc6582018-09-04 20:47:13 +0800212
213 process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
214 stderr=subprocess.STDOUT)
215
216 boot_complete = False
217 for line in iter(process.stdout.readline, b''):
218 logger.debug(line.strip())
219 # cvd is still running and got boot complete.
220 if _BOOT_COMPLETE in line:
Sam Chiuafbc6582018-09-04 20:47:13 +0800221 boot_complete = True
222 break
223
224 if not boot_complete:
Sam Chiuafbc6582018-09-04 20:47:13 +0800225 raise errors.LaunchCVDFail(
226 "Can't launch cuttlefish AVD. No %s found" % _BOOT_COMPLETE)
227
228 @staticmethod
229 def _IsLaunchCVDInUse():
230 """Check if launch_cvd is running.
231
232 Returns:
233 Boolean, True if launch_cvd is running. False otherwise.
234 """
235 try:
herbertxue61007092018-10-10 11:56:57 +0800236 with open(os.devnull, "w") as dev_null:
237 subprocess.check_call([_CMD_PGREP, constants.CMD_LAUNCH_CVD],
238 stderr=dev_null, stdout=dev_null)
Sam Chiuafbc6582018-09-04 20:47:13 +0800239 return True
240 except subprocess.CalledProcessError:
241 # launch_cvd process is not in use.
242 return False