blob: a3cdd87cc24d6b9f214898e9cafb9955f2634cdd [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
Kevin Chengae7a49d2018-10-18 14:11:22 -070078 # TODO(b/117366819): Should return the correct device serial according
79 # to --serial_number.
80 result_report = report.Report("local")
81 result_report.SetStatus(report.Status.SUCCESS)
82 result_report.AddData(key="devices",
Sam Chiu7a477f52018-10-22 11:20:36 +080083 value={"adb_port": constants.DEFAULT_ADB_PORT,
84 constants.VNC_PORT: constants.DEFAULT_VNC_PORT})
85 # Launch vnc client if we're auto-connecting.
86 if avd_spec.autoconnect:
87 utils.LaunchVNCFromReport(result_report, avd_spec)
Kevin Chengae7a49d2018-10-18 14:11:22 -070088 return result_report
Sam Chiuafbc6582018-09-04 20:47:13 +080089
90 @staticmethod
91 def GetImageArtifactsPath(avd_spec):
92 """Get image artifacts path.
93
94 This method will check if local image and launch_cvd are exist and
95 return the tuple path where they are located respectively.
96 For remote image, RemoteImageLocalInstance will override this method and
97 return the artifacts path which is extracted and downloaded from remote.
98
99 Args:
100 avd_spec: AVDSpec object that tells us what we're going to create.
101
102 Returns:
103 Tuple of (local image file, launch_cvd package) paths.
104 """
105 try:
106 # Check if local image is exist.
107 create_common.VerifyLocalImageArtifactsExist(
108 avd_spec.local_image_dir)
109
110 # TODO(b/117306227): help user to build out images and host package if
111 # anything needed is not found.
112 except errors.GetLocalImageError as imgerror:
113 logger.error(imgerror.message)
114 raise imgerror
115
116 # Check if launch_cvd is exist.
117 launch_cvd_path = os.path.join(
herbertxue61007092018-10-10 11:56:57 +0800118 os.environ.get(_ENV_ANDROID_HOST_OUT), "bin", constants.CMD_LAUNCH_CVD)
Sam Chiuafbc6582018-09-04 20:47:13 +0800119 if not os.path.exists(launch_cvd_path):
120 raise errors.GetCvdLocalHostPackageError(
121 "No launch_cvd found. Please run \"m launch_cvd\" first")
122
123 return avd_spec.local_image_dir, launch_cvd_path
124
125 @staticmethod
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700126 def _AddUserGroupsToCmd(cmd):
127 """Add the user groups to the command if necessary.
Sam Chiuafbc6582018-09-04 20:47:13 +0800128
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700129 As part of local host setup to enable local instance support,
130 the user is added to certain groups. For those settings to
131 take effect systemwide requires the user to log out and
132 log back in. In the scenario where the user has run setup and
133 hasn't logged out, we still want them to be able to launch a
134 local instance so add the user to the groups as part of the
135 command to ensure success.
136
137 The reason using here-doc instead of '&' is all operations need to be
138 ran in ths same pid. Here's an example cmd:
Sam Chiuafbc6582018-09-04 20:47:13 +0800139 $ sg kvm << EOF
140 sg libvirt
141 sg cvdnetwork
142 launch_cvd --cpus 2 --x_res 1280 --y_res 720 --dpi 160 --memory_mb 4096
143 EOF
144
145 Args:
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700146 cmd: String of the command to prepend the user groups to.
147
148 Returns:
149 String of the command with the user groups prepended to it if
150 necessary, otherwise the same existing command.
151 """
152 user_group_cmd = ""
153 host_setup = host_setup_runner.CuttlefishHostSetup()
154 if not host_setup.CheckUserInGroups(constants.LIST_CF_USER_GROUPS):
155 logger.debug("Need to add user groups to the command")
156 for idx, group in enumerate(constants.LIST_CF_USER_GROUPS):
157 user_group_cmd += _CMD_SG + group
158 if idx == 0:
159 user_group_cmd += " <<EOF\n"
160 else:
161 user_group_cmd += "\n"
162 cmd += "\nEOF"
163 user_group_cmd += cmd
164 logger.debug("user group cmd: %s", user_group_cmd)
165 return user_group_cmd
166
167 def PrepareLaunchCVDCmd(self, launch_cvd_path, hw_property,
168 system_image_dir, flavor):
169 """Prepare launch_cvd command.
170
171 Create the launch_cvd commands with all the required args and add
172 in the user groups to it if necessary.
173
174 Args:
Sam Chiuafbc6582018-09-04 20:47:13 +0800175 launch_cvd_path: String of launch_cvd path.
176 hw_property: dict object of hw property.
177 system_image_dir: String of local images path.
178 flavor: String of flavor type.
179
180 Returns:
181 String, launch_cvd cmd.
182 """
183 launch_cvd_w_args = launch_cvd_path + _CMD_LAUNCH_CVD_ARGS % (
184 hw_property["cpu"], hw_property["x_res"], hw_property["y_res"],
185 hw_property["dpi"], hw_property["memory"], hw_property["disk"],
cylan66713722018-10-06 01:38:26 +0800186 system_image_dir, constants.DEFAULT_VNC_PORT,
187 _CVD_SERIAL_PREFIX+flavor)
Sam Chiuafbc6582018-09-04 20:47:13 +0800188
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700189 launch_cmd = self._AddUserGroupsToCmd(launch_cvd_w_args)
190 logger.debug("launch_cvd cmd:\n %s", launch_cmd)
191 return launch_cmd
Sam Chiuafbc6582018-09-04 20:47:13 +0800192
herbertxue61007092018-10-10 11:56:57 +0800193 @utils.TimeExecute(function_description="Waiting for AVD(s) to boot up")
194 def CheckLaunchCVD(self, cmd, host_pack_dir):
Sam Chiuafbc6582018-09-04 20:47:13 +0800195 """Execute launch_cvd command and wait for boot up completed.
196
197 Args:
198 cmd: String, launch_cvd command.
herbertxue61007092018-10-10 11:56:57 +0800199 host_pack_dir: String of host package directory.
Sam Chiuafbc6582018-09-04 20:47:13 +0800200 """
Sam Chiuafbc6582018-09-04 20:47:13 +0800201 # Cuttlefish support launch single AVD at one time currently.
202 if self._IsLaunchCVDInUse():
203 logger.info("Cuttlefish AVD is already running.")
204 if utils.GetUserAnswerYes(_CONFIRM_RELAUNCH):
herbertxue61007092018-10-10 11:56:57 +0800205 stop_cvd_cmd = os.path.join(host_pack_dir, _CMD_STOP_CVD)
206 with open(os.devnull, "w") as dev_null:
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700207 subprocess.check_call(
208 self._AddUserGroupsToCmd(stop_cvd_cmd),
209 stderr=dev_null, stdout=dev_null, shell=True)
herbertxue61007092018-10-10 11:56:57 +0800210 else:
211 print("Exiting out")
212 sys.exit()
Sam Chiuafbc6582018-09-04 20:47:13 +0800213
214 process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
215 stderr=subprocess.STDOUT)
216
217 boot_complete = False
218 for line in iter(process.stdout.readline, b''):
219 logger.debug(line.strip())
220 # cvd is still running and got boot complete.
221 if _BOOT_COMPLETE in line:
Sam Chiuafbc6582018-09-04 20:47:13 +0800222 boot_complete = True
223 break
224
225 if not boot_complete:
Sam Chiuafbc6582018-09-04 20:47:13 +0800226 raise errors.LaunchCVDFail(
227 "Can't launch cuttlefish AVD. No %s found" % _BOOT_COMPLETE)
228
229 @staticmethod
230 def _IsLaunchCVDInUse():
231 """Check if launch_cvd is running.
232
233 Returns:
234 Boolean, True if launch_cvd is running. False otherwise.
235 """
236 try:
herbertxue61007092018-10-10 11:56:57 +0800237 with open(os.devnull, "w") as dev_null:
238 subprocess.check_call([_CMD_PGREP, constants.CMD_LAUNCH_CVD],
239 stderr=dev_null, stdout=dev_null)
Sam Chiuafbc6582018-09-04 20:47:13 +0800240 return True
241 except subprocess.CalledProcessError:
242 # launch_cvd process is not in use.
243 return False