blob: 7fcf0aac406c96468310944911fac43e3a560306 [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 +080027import time
28
29from acloud import errors
Kevin Cheng3ce4b452018-08-23 14:47:22 -070030from acloud.create import base_avd_create
Sam Chiuafbc6582018-09-04 20:47:13 +080031from acloud.create import create_common
32from acloud.internal import constants
33from acloud.internal.lib import utils
34from 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"
50 "Enter [y] to terminate current instance and launch a new "
51 "instance, enter anything else to exit out: ")
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 Cheng3ce4b452018-08-23 14:47:22 -070059 def Create(self, avd_spec):
60 """Create the AVD.
61
62 Args:
63 avd_spec: AVDSpec object that tells us what we're going to create.
64 """
Sam Chiuafbc6582018-09-04 20:47:13 +080065 self.PrintAvdDetails(avd_spec)
66 start = time.time()
67
68 local_image_path, launch_cvd_path = self.GetImageArtifactsPath(avd_spec)
69
70 cmd = self.PrepareLaunchCVDCmd(launch_cvd_path,
71 avd_spec.hw_property,
72 local_image_path,
73 avd_spec.flavor)
74 try:
herbertxue61007092018-10-10 11:56:57 +080075 self.CheckLaunchCVD(cmd, os.path.dirname(launch_cvd_path))
Sam Chiuafbc6582018-09-04 20:47:13 +080076 except errors.LaunchCVDFail as launch_error:
77 raise launch_error
78
79 utils.PrintColorString("\n")
80 utils.PrintColorString("Total time: %ds" % (time.time() - start),
81 utils.TextColors.WARNING)
82 # TODO(b/117366819): Should display the correct device serial
83 # according to the args --serial_number.
cylan4b8eacf2018-10-11 11:13:13 +080084 utils.PrintColorString("Device serial: %s" %
85 (constants.LOCALHOST_ADB_SERIAL %
Kevin Cheng065fdd62018-10-09 14:27:22 -070086 constants.DEFAULT_ADB_PORT),
Sam Chiuafbc6582018-09-04 20:47:13 +080087 utils.TextColors.WARNING)
88 if avd_spec.autoconnect:
89 self.LaunchVncClient()
90
91
92 @staticmethod
93 def GetImageArtifactsPath(avd_spec):
94 """Get image artifacts path.
95
96 This method will check if local image and launch_cvd are exist and
97 return the tuple path where they are located respectively.
98 For remote image, RemoteImageLocalInstance will override this method and
99 return the artifacts path which is extracted and downloaded from remote.
100
101 Args:
102 avd_spec: AVDSpec object that tells us what we're going to create.
103
104 Returns:
105 Tuple of (local image file, launch_cvd package) paths.
106 """
107 try:
108 # Check if local image is exist.
109 create_common.VerifyLocalImageArtifactsExist(
110 avd_spec.local_image_dir)
111
112 # TODO(b/117306227): help user to build out images and host package if
113 # anything needed is not found.
114 except errors.GetLocalImageError as imgerror:
115 logger.error(imgerror.message)
116 raise imgerror
117
118 # Check if launch_cvd is exist.
119 launch_cvd_path = os.path.join(
herbertxue61007092018-10-10 11:56:57 +0800120 os.environ.get(_ENV_ANDROID_HOST_OUT), "bin", constants.CMD_LAUNCH_CVD)
Sam Chiuafbc6582018-09-04 20:47:13 +0800121 if not os.path.exists(launch_cvd_path):
122 raise errors.GetCvdLocalHostPackageError(
123 "No launch_cvd found. Please run \"m launch_cvd\" first")
124
125 return avd_spec.local_image_dir, launch_cvd_path
126
127 @staticmethod
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700128 def _AddUserGroupsToCmd(cmd):
129 """Add the user groups to the command if necessary.
Sam Chiuafbc6582018-09-04 20:47:13 +0800130
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700131 As part of local host setup to enable local instance support,
132 the user is added to certain groups. For those settings to
133 take effect systemwide requires the user to log out and
134 log back in. In the scenario where the user has run setup and
135 hasn't logged out, we still want them to be able to launch a
136 local instance so add the user to the groups as part of the
137 command to ensure success.
138
139 The reason using here-doc instead of '&' is all operations need to be
140 ran in ths same pid. Here's an example cmd:
Sam Chiuafbc6582018-09-04 20:47:13 +0800141 $ sg kvm << EOF
142 sg libvirt
143 sg cvdnetwork
144 launch_cvd --cpus 2 --x_res 1280 --y_res 720 --dpi 160 --memory_mb 4096
145 EOF
146
147 Args:
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700148 cmd: String of the command to prepend the user groups to.
149
150 Returns:
151 String of the command with the user groups prepended to it if
152 necessary, otherwise the same existing command.
153 """
154 user_group_cmd = ""
155 host_setup = host_setup_runner.CuttlefishHostSetup()
156 if not host_setup.CheckUserInGroups(constants.LIST_CF_USER_GROUPS):
157 logger.debug("Need to add user groups to the command")
158 for idx, group in enumerate(constants.LIST_CF_USER_GROUPS):
159 user_group_cmd += _CMD_SG + group
160 if idx == 0:
161 user_group_cmd += " <<EOF\n"
162 else:
163 user_group_cmd += "\n"
164 cmd += "\nEOF"
165 user_group_cmd += cmd
166 logger.debug("user group cmd: %s", user_group_cmd)
167 return user_group_cmd
168
169 def PrepareLaunchCVDCmd(self, launch_cvd_path, hw_property,
170 system_image_dir, flavor):
171 """Prepare launch_cvd command.
172
173 Create the launch_cvd commands with all the required args and add
174 in the user groups to it if necessary.
175
176 Args:
Sam Chiuafbc6582018-09-04 20:47:13 +0800177 launch_cvd_path: String of launch_cvd path.
178 hw_property: dict object of hw property.
179 system_image_dir: String of local images path.
180 flavor: String of flavor type.
181
182 Returns:
183 String, launch_cvd cmd.
184 """
185 launch_cvd_w_args = launch_cvd_path + _CMD_LAUNCH_CVD_ARGS % (
186 hw_property["cpu"], hw_property["x_res"], hw_property["y_res"],
187 hw_property["dpi"], hw_property["memory"], hw_property["disk"],
cylan66713722018-10-06 01:38:26 +0800188 system_image_dir, constants.DEFAULT_VNC_PORT,
189 _CVD_SERIAL_PREFIX+flavor)
Sam Chiuafbc6582018-09-04 20:47:13 +0800190
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700191 launch_cmd = self._AddUserGroupsToCmd(launch_cvd_w_args)
192 logger.debug("launch_cvd cmd:\n %s", launch_cmd)
193 return launch_cmd
Sam Chiuafbc6582018-09-04 20:47:13 +0800194
herbertxue61007092018-10-10 11:56:57 +0800195 @utils.TimeExecute(function_description="Waiting for AVD(s) to boot up")
196 def CheckLaunchCVD(self, cmd, host_pack_dir):
Sam Chiuafbc6582018-09-04 20:47:13 +0800197 """Execute launch_cvd command and wait for boot up completed.
198
199 Args:
200 cmd: String, launch_cvd command.
herbertxue61007092018-10-10 11:56:57 +0800201 host_pack_dir: String of host package directory.
Sam Chiuafbc6582018-09-04 20:47:13 +0800202 """
Sam Chiuafbc6582018-09-04 20:47:13 +0800203 # Cuttlefish support launch single AVD at one time currently.
204 if self._IsLaunchCVDInUse():
205 logger.info("Cuttlefish AVD is already running.")
206 if utils.GetUserAnswerYes(_CONFIRM_RELAUNCH):
herbertxue61007092018-10-10 11:56:57 +0800207 stop_cvd_cmd = os.path.join(host_pack_dir, _CMD_STOP_CVD)
208 with open(os.devnull, "w") as dev_null:
Kevin Cheng2aa41da2018-10-11 15:00:48 -0700209 subprocess.check_call(
210 self._AddUserGroupsToCmd(stop_cvd_cmd),
211 stderr=dev_null, stdout=dev_null, shell=True)
herbertxue61007092018-10-10 11:56:57 +0800212 else:
213 print("Exiting out")
214 sys.exit()
Sam Chiuafbc6582018-09-04 20:47:13 +0800215
216 process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
217 stderr=subprocess.STDOUT)
218
219 boot_complete = False
220 for line in iter(process.stdout.readline, b''):
221 logger.debug(line.strip())
222 # cvd is still running and got boot complete.
223 if _BOOT_COMPLETE in line:
Sam Chiuafbc6582018-09-04 20:47:13 +0800224 boot_complete = True
225 break
226
227 if not boot_complete:
Sam Chiuafbc6582018-09-04 20:47:13 +0800228 raise errors.LaunchCVDFail(
229 "Can't launch cuttlefish AVD. No %s found" % _BOOT_COMPLETE)
230
231 @staticmethod
232 def _IsLaunchCVDInUse():
233 """Check if launch_cvd is running.
234
235 Returns:
236 Boolean, True if launch_cvd is running. False otherwise.
237 """
238 try:
herbertxue61007092018-10-10 11:56:57 +0800239 with open(os.devnull, "w") as dev_null:
240 subprocess.check_call([_CMD_PGREP, constants.CMD_LAUNCH_CVD],
241 stderr=dev_null, stdout=dev_null)
Sam Chiuafbc6582018-09-04 20:47:13 +0800242 return True
243 except subprocess.CalledProcessError:
244 # launch_cvd process is not in use.
245 return False