xref: /aosp_15_r20/tools/acloud/list/instance.py (revision 800a58d989c669b8eb8a71d8df53b1ba3d411444)
1*800a58d9SAndroid Build Coastguard Worker# Copyright 2018 - The Android Open Source Project
2*800a58d9SAndroid Build Coastguard Worker#
3*800a58d9SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*800a58d9SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*800a58d9SAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*800a58d9SAndroid Build Coastguard Worker#
7*800a58d9SAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
8*800a58d9SAndroid Build Coastguard Worker#
9*800a58d9SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*800a58d9SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*800a58d9SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*800a58d9SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*800a58d9SAndroid Build Coastguard Worker# limitations under the License.
14*800a58d9SAndroid Build Coastguard Workerr"""Instance class.
15*800a58d9SAndroid Build Coastguard Worker
16*800a58d9SAndroid Build Coastguard WorkerDefine the instance class used to hold details about an AVD instance.
17*800a58d9SAndroid Build Coastguard Worker
18*800a58d9SAndroid Build Coastguard WorkerThe instance class will hold details about AVD instances (remote/local) used to
19*800a58d9SAndroid Build Coastguard Workerenable users to understand what instances they've created. This will be leveraged
20*800a58d9SAndroid Build Coastguard Workerfor the list, delete, and reconnect commands.
21*800a58d9SAndroid Build Coastguard Worker
22*800a58d9SAndroid Build Coastguard WorkerThe details include:
23*800a58d9SAndroid Build Coastguard Worker- instance name (for remote instances)
24*800a58d9SAndroid Build Coastguard Worker- creation date/instance duration
25*800a58d9SAndroid Build Coastguard Worker- instance image details (branch/target/build id)
26*800a58d9SAndroid Build Coastguard Worker- and more!
27*800a58d9SAndroid Build Coastguard Worker"""
28*800a58d9SAndroid Build Coastguard Worker
29*800a58d9SAndroid Build Coastguard Workerimport collections
30*800a58d9SAndroid Build Coastguard Workerimport datetime
31*800a58d9SAndroid Build Coastguard Workerimport json
32*800a58d9SAndroid Build Coastguard Workerimport logging
33*800a58d9SAndroid Build Coastguard Workerimport os
34*800a58d9SAndroid Build Coastguard Workerimport re
35*800a58d9SAndroid Build Coastguard Workerimport subprocess
36*800a58d9SAndroid Build Coastguard Workerimport tempfile
37*800a58d9SAndroid Build Coastguard Worker
38*800a58d9SAndroid Build Coastguard Worker# pylint: disable=import-error,too-many-lines
39*800a58d9SAndroid Build Coastguard Workerimport dateutil.parser
40*800a58d9SAndroid Build Coastguard Workerimport dateutil.tz
41*800a58d9SAndroid Build Coastguard Worker
42*800a58d9SAndroid Build Coastguard Workerfrom acloud.create import local_image_local_instance
43*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal import constants
44*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import cvd_runtime_config
45*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import utils
46*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib.adb_tools import AdbTools
47*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib.local_instance_lock import LocalInstanceLock
48*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib.gcompute_client import GetInstanceIP
49*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib.gcompute_client import GetGCEHostName
50*800a58d9SAndroid Build Coastguard Worker
51*800a58d9SAndroid Build Coastguard Worker
52*800a58d9SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__)
53*800a58d9SAndroid Build Coastguard Worker
54*800a58d9SAndroid Build Coastguard Worker_ACLOUD_CVD_TEMP = os.path.join(tempfile.gettempdir(), "acloud_cvd_temp")
55*800a58d9SAndroid Build Coastguard Worker_CFG_KEY_INSTANCES = "instances"
56*800a58d9SAndroid Build Coastguard Worker_CVD_RUNTIME_FOLDER_NAME = "cuttlefish_runtime"
57*800a58d9SAndroid Build Coastguard Worker_CVD_BIN = "cvd"
58*800a58d9SAndroid Build Coastguard Worker_CVD_BIN_FOLDER = "host_bins/bin"
59*800a58d9SAndroid Build Coastguard Worker_CVD_STATUS_BIN = "cvd_status"
60*800a58d9SAndroid Build Coastguard Worker_CVD_STOP_ERROR_KEYWORDS = "cvd_internal_stop E"
61*800a58d9SAndroid Build Coastguard Worker# Default timeout 30 secs for cvd commands.
62*800a58d9SAndroid Build Coastguard Worker_CVD_TIMEOUT = 30
63*800a58d9SAndroid Build Coastguard Worker# Keywords read from runtime config.
64*800a58d9SAndroid Build Coastguard Worker_ADB_HOST_PORT = "adb_host_port"
65*800a58d9SAndroid Build Coastguard Worker# Keywords read from the output of "cvd status".
66*800a58d9SAndroid Build Coastguard Worker_DISPLAYS = "displays"
67*800a58d9SAndroid Build Coastguard Worker_WEBRTC_PORT = "webrtc_port"
68*800a58d9SAndroid Build Coastguard Worker_ADB_SERIAL = "adb_serial"
69*800a58d9SAndroid Build Coastguard Worker_INSTANCE_ASSEMBLY_DIR = "cuttlefish_assembly"
70*800a58d9SAndroid Build Coastguard Worker_LOCAL_INSTANCE_NAME_FORMAT = "local-instance-%(id)d"
71*800a58d9SAndroid Build Coastguard Worker_LOCAL_INSTANCE_NAME_PATTERN = re.compile(r"^local-instance-(?P<id>\d+)$")
72*800a58d9SAndroid Build Coastguard Worker_ACLOUDWEB_INSTANCE_START_STRING = "cf-"
73*800a58d9SAndroid Build Coastguard Worker_MSG_UNABLE_TO_CALCULATE = "Unable to calculate"
74*800a58d9SAndroid Build Coastguard Worker_NO_ANDROID_ENV = "android source not available"
75*800a58d9SAndroid Build Coastguard Worker_RE_GROUP_ADB = "local_adb_port"
76*800a58d9SAndroid Build Coastguard Worker_RE_GROUP_VNC = "local_vnc_port"
77*800a58d9SAndroid Build Coastguard Worker_RE_SSH_TUNNEL_PATTERN = (r"((.*\s*-L\s)(?P<%s>\d+):127.0.0.1:%s)"
78*800a58d9SAndroid Build Coastguard Worker                          r"((.*\s*-L\s)(?P<%s>\d+):127.0.0.1:%s)"
79*800a58d9SAndroid Build Coastguard Worker                          r"(.+(%s|%s))")
80*800a58d9SAndroid Build Coastguard Worker_RE_TIMEZONE = re.compile(r"^(?P<time>[0-9\-\.:T]*)(?P<timezone>[+-]\d+:\d+)$")
81*800a58d9SAndroid Build Coastguard Worker_RE_DEVICE_INFO = re.compile(r"(?s).*(?P<device_info>[{][\s\w\W]+})")
82*800a58d9SAndroid Build Coastguard Worker
83*800a58d9SAndroid Build Coastguard Worker_COMMAND_PS_LAUNCH_CVD = ["ps", "-wweo", "lstart,cmd"]
84*800a58d9SAndroid Build Coastguard Worker_RE_RUN_CVD = re.compile(r"(?P<date_str>^[^/]+)(.*run_cvd)")
85*800a58d9SAndroid Build Coastguard Worker_X_RES = "x_res"
86*800a58d9SAndroid Build Coastguard Worker_Y_RES = "y_res"
87*800a58d9SAndroid Build Coastguard Worker_DPI = "dpi"
88*800a58d9SAndroid Build Coastguard Worker_DISPLAY_STRING = "%(x_res)sx%(y_res)s (%(dpi)s)"
89*800a58d9SAndroid Build Coastguard Worker_RE_ZONE = re.compile(r".+/zones/(?P<zone>.+)$")
90*800a58d9SAndroid Build Coastguard Worker_RE_PROJECT = re.compile(r".+/projects/(?P<project>.+)/zones/.+$")
91*800a58d9SAndroid Build Coastguard Worker_LOCAL_ZONE = "local"
92*800a58d9SAndroid Build Coastguard Worker_INDENT = " " * 3
93*800a58d9SAndroid Build Coastguard WorkerLocalPorts = collections.namedtuple("LocalPorts", [constants.VNC_PORT,
94*800a58d9SAndroid Build Coastguard Worker                                                   constants.ADB_PORT])
95*800a58d9SAndroid Build Coastguard Worker
96*800a58d9SAndroid Build Coastguard Worker
97*800a58d9SAndroid Build Coastguard Workerdef GetDefaultCuttlefishConfig():
98*800a58d9SAndroid Build Coastguard Worker    """Get the path of default cuttlefish instance config.
99*800a58d9SAndroid Build Coastguard Worker
100*800a58d9SAndroid Build Coastguard Worker    Return:
101*800a58d9SAndroid Build Coastguard Worker        String, path of cf runtime config.
102*800a58d9SAndroid Build Coastguard Worker    """
103*800a58d9SAndroid Build Coastguard Worker    cfg_path = os.path.join(os.path.expanduser("~"), _CVD_RUNTIME_FOLDER_NAME,
104*800a58d9SAndroid Build Coastguard Worker                            constants.CUTTLEFISH_CONFIG_FILE)
105*800a58d9SAndroid Build Coastguard Worker    if os.path.isfile(cfg_path):
106*800a58d9SAndroid Build Coastguard Worker        return cfg_path
107*800a58d9SAndroid Build Coastguard Worker    return None
108*800a58d9SAndroid Build Coastguard Worker
109*800a58d9SAndroid Build Coastguard Worker
110*800a58d9SAndroid Build Coastguard Workerdef GetLocalInstanceName(local_instance_id):
111*800a58d9SAndroid Build Coastguard Worker    """Get local cuttlefish instance name by instance id.
112*800a58d9SAndroid Build Coastguard Worker
113*800a58d9SAndroid Build Coastguard Worker    Args:
114*800a58d9SAndroid Build Coastguard Worker        local_instance_id: Integer of instance id.
115*800a58d9SAndroid Build Coastguard Worker
116*800a58d9SAndroid Build Coastguard Worker    Return:
117*800a58d9SAndroid Build Coastguard Worker        String, the instance name.
118*800a58d9SAndroid Build Coastguard Worker    """
119*800a58d9SAndroid Build Coastguard Worker    return _LOCAL_INSTANCE_NAME_FORMAT % {"id": local_instance_id}
120*800a58d9SAndroid Build Coastguard Worker
121*800a58d9SAndroid Build Coastguard Worker
122*800a58d9SAndroid Build Coastguard Workerdef GetLocalInstanceIdByName(name):
123*800a58d9SAndroid Build Coastguard Worker    """Get local cuttlefish instance id by name.
124*800a58d9SAndroid Build Coastguard Worker
125*800a58d9SAndroid Build Coastguard Worker    Args:
126*800a58d9SAndroid Build Coastguard Worker        name: String of instance name.
127*800a58d9SAndroid Build Coastguard Worker
128*800a58d9SAndroid Build Coastguard Worker    Return:
129*800a58d9SAndroid Build Coastguard Worker        The instance id as an integer if the name is in valid format.
130*800a58d9SAndroid Build Coastguard Worker        None if the name does not represent a local cuttlefish instance.
131*800a58d9SAndroid Build Coastguard Worker    """
132*800a58d9SAndroid Build Coastguard Worker    match = _LOCAL_INSTANCE_NAME_PATTERN.match(name)
133*800a58d9SAndroid Build Coastguard Worker    if match:
134*800a58d9SAndroid Build Coastguard Worker        return int(match.group("id"))
135*800a58d9SAndroid Build Coastguard Worker    return None
136*800a58d9SAndroid Build Coastguard Worker
137*800a58d9SAndroid Build Coastguard Worker
138*800a58d9SAndroid Build Coastguard Workerdef GetLocalInstanceConfigPath(local_instance_id):
139*800a58d9SAndroid Build Coastguard Worker    """Get the path of instance config.
140*800a58d9SAndroid Build Coastguard Worker
141*800a58d9SAndroid Build Coastguard Worker    Args:
142*800a58d9SAndroid Build Coastguard Worker        local_instance_id: Integer of instance id.
143*800a58d9SAndroid Build Coastguard Worker
144*800a58d9SAndroid Build Coastguard Worker    Return:
145*800a58d9SAndroid Build Coastguard Worker        String, path of cf runtime config.
146*800a58d9SAndroid Build Coastguard Worker    """
147*800a58d9SAndroid Build Coastguard Worker    ins_assembly_dir = os.path.join(GetLocalInstanceHomeDir(local_instance_id),
148*800a58d9SAndroid Build Coastguard Worker                                    _INSTANCE_ASSEMBLY_DIR)
149*800a58d9SAndroid Build Coastguard Worker    return os.path.join(ins_assembly_dir, constants.CUTTLEFISH_CONFIG_FILE)
150*800a58d9SAndroid Build Coastguard Worker
151*800a58d9SAndroid Build Coastguard Worker
152*800a58d9SAndroid Build Coastguard Workerdef GetLocalInstanceConfig(local_instance_id):
153*800a58d9SAndroid Build Coastguard Worker    """Get the path of existed config from local instance.
154*800a58d9SAndroid Build Coastguard Worker
155*800a58d9SAndroid Build Coastguard Worker    Args:
156*800a58d9SAndroid Build Coastguard Worker        local_instance_id: Integer of instance id.
157*800a58d9SAndroid Build Coastguard Worker
158*800a58d9SAndroid Build Coastguard Worker    Return:
159*800a58d9SAndroid Build Coastguard Worker        String, path of cf runtime config. None for config not exist.
160*800a58d9SAndroid Build Coastguard Worker    """
161*800a58d9SAndroid Build Coastguard Worker    cfg_path = GetLocalInstanceConfigPath(local_instance_id)
162*800a58d9SAndroid Build Coastguard Worker    if os.path.isfile(cfg_path):
163*800a58d9SAndroid Build Coastguard Worker        return cfg_path
164*800a58d9SAndroid Build Coastguard Worker    return None
165*800a58d9SAndroid Build Coastguard Worker
166*800a58d9SAndroid Build Coastguard Workerdef GetInstanceId(cfg_path):
167*800a58d9SAndroid Build Coastguard Worker    """Get the cuttlefish ID from config file.
168*800a58d9SAndroid Build Coastguard Worker
169*800a58d9SAndroid Build Coastguard Worker    Args:
170*800a58d9SAndroid Build Coastguard Worker        cfg_path: Path to config file.
171*800a58d9SAndroid Build Coastguard Worker
172*800a58d9SAndroid Build Coastguard Worker    Return:
173*800a58d9SAndroid Build Coastguard Worker        Cuttlefish ID.
174*800a58d9SAndroid Build Coastguard Worker    """
175*800a58d9SAndroid Build Coastguard Worker    with open(cfg_path, "r") as cf_config:
176*800a58d9SAndroid Build Coastguard Worker        config_dict = json.load(cf_config)
177*800a58d9SAndroid Build Coastguard Worker        instances = config_dict.get(_CFG_KEY_INSTANCES)
178*800a58d9SAndroid Build Coastguard Worker        ins_id = int(min(instances.keys())) if instances else 1
179*800a58d9SAndroid Build Coastguard Worker        return ins_id
180*800a58d9SAndroid Build Coastguard Worker
181*800a58d9SAndroid Build Coastguard Workerdef GetAllLocalInstanceConfigs():
182*800a58d9SAndroid Build Coastguard Worker    """Get all cuttlefish runtime configs from the known locations.
183*800a58d9SAndroid Build Coastguard Worker
184*800a58d9SAndroid Build Coastguard Worker    Return:
185*800a58d9SAndroid Build Coastguard Worker        List of tuples. Each tuple consists of an instance id and a config
186*800a58d9SAndroid Build Coastguard Worker        path.
187*800a58d9SAndroid Build Coastguard Worker    """
188*800a58d9SAndroid Build Coastguard Worker    id_cfg_pairs = []
189*800a58d9SAndroid Build Coastguard Worker    id_set = set()
190*800a58d9SAndroid Build Coastguard Worker    # Check if any instance config is under home folder.
191*800a58d9SAndroid Build Coastguard Worker    cfg_path = GetDefaultCuttlefishConfig()
192*800a58d9SAndroid Build Coastguard Worker    if cfg_path:
193*800a58d9SAndroid Build Coastguard Worker        ins_id = GetInstanceId(cfg_path)
194*800a58d9SAndroid Build Coastguard Worker
195*800a58d9SAndroid Build Coastguard Worker        # skip redundant id in HOME and /tmp
196*800a58d9SAndroid Build Coastguard Worker        if ins_id not in id_set:
197*800a58d9SAndroid Build Coastguard Worker            id_set.add(ins_id)
198*800a58d9SAndroid Build Coastguard Worker            id_cfg_pairs.append((ins_id, cfg_path))
199*800a58d9SAndroid Build Coastguard Worker
200*800a58d9SAndroid Build Coastguard Worker    # Check if any instance config is under acloud cvd temp folder.
201*800a58d9SAndroid Build Coastguard Worker    if os.path.exists(_ACLOUD_CVD_TEMP):
202*800a58d9SAndroid Build Coastguard Worker        for ins_name in os.listdir(_ACLOUD_CVD_TEMP):
203*800a58d9SAndroid Build Coastguard Worker            ins_id = GetLocalInstanceIdByName(ins_name)
204*800a58d9SAndroid Build Coastguard Worker            if ins_id is not None and ins_id not in id_set:
205*800a58d9SAndroid Build Coastguard Worker                cfg_path = GetLocalInstanceConfig(ins_id)
206*800a58d9SAndroid Build Coastguard Worker                if cfg_path:
207*800a58d9SAndroid Build Coastguard Worker                    id_set.add(ins_id)
208*800a58d9SAndroid Build Coastguard Worker                    id_cfg_pairs.append((ins_id, cfg_path))
209*800a58d9SAndroid Build Coastguard Worker    return id_cfg_pairs
210*800a58d9SAndroid Build Coastguard Worker
211*800a58d9SAndroid Build Coastguard Worker
212*800a58d9SAndroid Build Coastguard Workerdef GetLocalInstanceHomeDir(local_instance_id):
213*800a58d9SAndroid Build Coastguard Worker    """Get local instance home dir according to instance id.
214*800a58d9SAndroid Build Coastguard Worker
215*800a58d9SAndroid Build Coastguard Worker    Args:
216*800a58d9SAndroid Build Coastguard Worker        local_instance_id: Integer of instance id.
217*800a58d9SAndroid Build Coastguard Worker
218*800a58d9SAndroid Build Coastguard Worker    Return:
219*800a58d9SAndroid Build Coastguard Worker        String, path of instance home dir.
220*800a58d9SAndroid Build Coastguard Worker    """
221*800a58d9SAndroid Build Coastguard Worker    return os.path.join(_ACLOUD_CVD_TEMP,
222*800a58d9SAndroid Build Coastguard Worker                        GetLocalInstanceName(local_instance_id))
223*800a58d9SAndroid Build Coastguard Worker
224*800a58d9SAndroid Build Coastguard Worker
225*800a58d9SAndroid Build Coastguard Workerdef GetLocalInstanceLock(local_instance_id):
226*800a58d9SAndroid Build Coastguard Worker    """Get local instance lock.
227*800a58d9SAndroid Build Coastguard Worker
228*800a58d9SAndroid Build Coastguard Worker    Args:
229*800a58d9SAndroid Build Coastguard Worker        local_instance_id: Integer of instance id.
230*800a58d9SAndroid Build Coastguard Worker
231*800a58d9SAndroid Build Coastguard Worker    Returns:
232*800a58d9SAndroid Build Coastguard Worker        LocalInstanceLock object.
233*800a58d9SAndroid Build Coastguard Worker    """
234*800a58d9SAndroid Build Coastguard Worker    file_path = os.path.join(_ACLOUD_CVD_TEMP,
235*800a58d9SAndroid Build Coastguard Worker                             GetLocalInstanceName(local_instance_id) + ".lock")
236*800a58d9SAndroid Build Coastguard Worker    return LocalInstanceLock(file_path)
237*800a58d9SAndroid Build Coastguard Worker
238*800a58d9SAndroid Build Coastguard Worker
239*800a58d9SAndroid Build Coastguard Workerdef GetLocalInstanceRuntimeDir(local_instance_id):
240*800a58d9SAndroid Build Coastguard Worker    """Get instance runtime dir
241*800a58d9SAndroid Build Coastguard Worker
242*800a58d9SAndroid Build Coastguard Worker    Args:
243*800a58d9SAndroid Build Coastguard Worker        local_instance_id: Integer of instance id.
244*800a58d9SAndroid Build Coastguard Worker
245*800a58d9SAndroid Build Coastguard Worker    Return:
246*800a58d9SAndroid Build Coastguard Worker        String, path of instance runtime dir.
247*800a58d9SAndroid Build Coastguard Worker    """
248*800a58d9SAndroid Build Coastguard Worker    return os.path.join(GetLocalInstanceHomeDir(local_instance_id),
249*800a58d9SAndroid Build Coastguard Worker                        _CVD_RUNTIME_FOLDER_NAME)
250*800a58d9SAndroid Build Coastguard Worker
251*800a58d9SAndroid Build Coastguard Worker
252*800a58d9SAndroid Build Coastguard Workerdef GetCuttleFishLocalInstances(cf_config_path):
253*800a58d9SAndroid Build Coastguard Worker    """Get all instances information from cf runtime config.
254*800a58d9SAndroid Build Coastguard Worker
255*800a58d9SAndroid Build Coastguard Worker    Args:
256*800a58d9SAndroid Build Coastguard Worker        cf_config_path: String, path to the cf runtime config.
257*800a58d9SAndroid Build Coastguard Worker
258*800a58d9SAndroid Build Coastguard Worker    Returns:
259*800a58d9SAndroid Build Coastguard Worker        List of LocalInstance object.
260*800a58d9SAndroid Build Coastguard Worker    """
261*800a58d9SAndroid Build Coastguard Worker    cf_runtime_cfg = cvd_runtime_config.CvdRuntimeConfig(cf_config_path)
262*800a58d9SAndroid Build Coastguard Worker    local_instances = []
263*800a58d9SAndroid Build Coastguard Worker    for ins_id in cf_runtime_cfg.instance_ids:
264*800a58d9SAndroid Build Coastguard Worker        local_instances.append(LocalInstance(cf_config_path, ins_id))
265*800a58d9SAndroid Build Coastguard Worker    return local_instances
266*800a58d9SAndroid Build Coastguard Worker
267*800a58d9SAndroid Build Coastguard Worker
268*800a58d9SAndroid Build Coastguard Workerdef _GetCurrentLocalTime():
269*800a58d9SAndroid Build Coastguard Worker    """Return a datetime object for current time in local time zone."""
270*800a58d9SAndroid Build Coastguard Worker    return datetime.datetime.now(dateutil.tz.tzlocal()).replace(microsecond=0)
271*800a58d9SAndroid Build Coastguard Worker
272*800a58d9SAndroid Build Coastguard Worker
273*800a58d9SAndroid Build Coastguard Workerdef _GetElapsedTime(start_time):
274*800a58d9SAndroid Build Coastguard Worker    """Calculate the elapsed time from start_time till now.
275*800a58d9SAndroid Build Coastguard Worker
276*800a58d9SAndroid Build Coastguard Worker    Args:
277*800a58d9SAndroid Build Coastguard Worker        start_time: String of instance created time.
278*800a58d9SAndroid Build Coastguard Worker
279*800a58d9SAndroid Build Coastguard Worker    Returns:
280*800a58d9SAndroid Build Coastguard Worker        datetime.timedelta of elapsed time, _MSG_UNABLE_TO_CALCULATE for
281*800a58d9SAndroid Build Coastguard Worker        datetime can't parse cases.
282*800a58d9SAndroid Build Coastguard Worker    """
283*800a58d9SAndroid Build Coastguard Worker    match = _RE_TIMEZONE.match(start_time)
284*800a58d9SAndroid Build Coastguard Worker    try:
285*800a58d9SAndroid Build Coastguard Worker        # Check start_time has timezone or not. If timezone can't be found,
286*800a58d9SAndroid Build Coastguard Worker        # use local timezone to get elapsed time.
287*800a58d9SAndroid Build Coastguard Worker        if match:
288*800a58d9SAndroid Build Coastguard Worker            return _GetCurrentLocalTime() - dateutil.parser.parse(
289*800a58d9SAndroid Build Coastguard Worker                start_time).replace(microsecond=0)
290*800a58d9SAndroid Build Coastguard Worker
291*800a58d9SAndroid Build Coastguard Worker        return _GetCurrentLocalTime() - dateutil.parser.parse(
292*800a58d9SAndroid Build Coastguard Worker            start_time).replace(tzinfo=dateutil.tz.tzlocal(), microsecond=0)
293*800a58d9SAndroid Build Coastguard Worker    except ValueError:
294*800a58d9SAndroid Build Coastguard Worker        logger.debug(("Can't parse datetime string(%s)."), start_time)
295*800a58d9SAndroid Build Coastguard Worker        return _MSG_UNABLE_TO_CALCULATE
296*800a58d9SAndroid Build Coastguard Worker
297*800a58d9SAndroid Build Coastguard Workerdef _GetDeviceFullName(device_serial, instance_name, elapsed_time,
298*800a58d9SAndroid Build Coastguard Worker                       webrtc_device_id=None):
299*800a58d9SAndroid Build Coastguard Worker    """Get the full name of device.
300*800a58d9SAndroid Build Coastguard Worker
301*800a58d9SAndroid Build Coastguard Worker    The full name is composed with device serial, webrtc device id, instance
302*800a58d9SAndroid Build Coastguard Worker    name, and elapsed_time.
303*800a58d9SAndroid Build Coastguard Worker
304*800a58d9SAndroid Build Coastguard Worker    Args:
305*800a58d9SAndroid Build Coastguard Worker        device_serial: String of device serial. e.g. 127.0.0.1:6520
306*800a58d9SAndroid Build Coastguard Worker        instance_name: String of instance name.
307*800a58d9SAndroid Build Coastguard Worker        elapsed time: String of elapsed time.
308*800a58d9SAndroid Build Coastguard Worker        webrtc_device_id: String of webrtc device id.
309*800a58d9SAndroid Build Coastguard Worker
310*800a58d9SAndroid Build Coastguard Worker    Returns:
311*800a58d9SAndroid Build Coastguard Worker        String of device full name.
312*800a58d9SAndroid Build Coastguard Worker    """
313*800a58d9SAndroid Build Coastguard Worker    if webrtc_device_id:
314*800a58d9SAndroid Build Coastguard Worker        return (f"device serial: {device_serial} {webrtc_device_id} "
315*800a58d9SAndroid Build Coastguard Worker                f"({instance_name}) elapsed time: {elapsed_time}")
316*800a58d9SAndroid Build Coastguard Worker
317*800a58d9SAndroid Build Coastguard Worker    return (f"device serial: {device_serial} ({instance_name}) "
318*800a58d9SAndroid Build Coastguard Worker            f"elapsed time: {elapsed_time}")
319*800a58d9SAndroid Build Coastguard Worker
320*800a58d9SAndroid Build Coastguard Worker
321*800a58d9SAndroid Build Coastguard Workerdef _IsProcessRunning(process):
322*800a58d9SAndroid Build Coastguard Worker    """Check if this process is running.
323*800a58d9SAndroid Build Coastguard Worker
324*800a58d9SAndroid Build Coastguard Worker    Returns:
325*800a58d9SAndroid Build Coastguard Worker        Boolean, True for this process is running.
326*800a58d9SAndroid Build Coastguard Worker    """
327*800a58d9SAndroid Build Coastguard Worker    match_pattern = re.compile(f"(.+)({process} )(.+)")
328*800a58d9SAndroid Build Coastguard Worker    process_output = utils.CheckOutput(constants.COMMAND_PS)
329*800a58d9SAndroid Build Coastguard Worker    for line in process_output.splitlines():
330*800a58d9SAndroid Build Coastguard Worker        process_match = match_pattern.match(line)
331*800a58d9SAndroid Build Coastguard Worker        if process_match:
332*800a58d9SAndroid Build Coastguard Worker            return True
333*800a58d9SAndroid Build Coastguard Worker    return False
334*800a58d9SAndroid Build Coastguard Worker
335*800a58d9SAndroid Build Coastguard Worker
336*800a58d9SAndroid Build Coastguard Worker# pylint: disable=useless-object-inheritance
337*800a58d9SAndroid Build Coastguard Workerclass Instance(object):
338*800a58d9SAndroid Build Coastguard Worker    """Class to store data of instance."""
339*800a58d9SAndroid Build Coastguard Worker
340*800a58d9SAndroid Build Coastguard Worker    # pylint: disable=too-many-locals
341*800a58d9SAndroid Build Coastguard Worker    def __init__(self, name, fullname, display, ip, status=None, adb_port=None,
342*800a58d9SAndroid Build Coastguard Worker                 vnc_port=None, ssh_tunnel_is_connected=None, createtime=None,
343*800a58d9SAndroid Build Coastguard Worker                 elapsed_time=None, avd_type=None, avd_flavor=None,
344*800a58d9SAndroid Build Coastguard Worker                 is_local=False, device_information=None, zone=None,
345*800a58d9SAndroid Build Coastguard Worker                 webrtc_port=None, webrtc_forward_port=None):
346*800a58d9SAndroid Build Coastguard Worker        self._name = name
347*800a58d9SAndroid Build Coastguard Worker        self._fullname = fullname
348*800a58d9SAndroid Build Coastguard Worker        self._status = status
349*800a58d9SAndroid Build Coastguard Worker        self._display = display  # Resolution and dpi
350*800a58d9SAndroid Build Coastguard Worker        self._ip = ip
351*800a58d9SAndroid Build Coastguard Worker        self._adb_port = adb_port  # adb port which is forwarding to remote
352*800a58d9SAndroid Build Coastguard Worker        self._vnc_port = vnc_port  # vnc port which is forwarding to remote
353*800a58d9SAndroid Build Coastguard Worker        self._webrtc_port = webrtc_port
354*800a58d9SAndroid Build Coastguard Worker        self._webrtc_forward_port = webrtc_forward_port
355*800a58d9SAndroid Build Coastguard Worker        # True if ssh tunnel is still connected
356*800a58d9SAndroid Build Coastguard Worker        self._ssh_tunnel_is_connected = ssh_tunnel_is_connected
357*800a58d9SAndroid Build Coastguard Worker        self._createtime = createtime
358*800a58d9SAndroid Build Coastguard Worker        self._elapsed_time = elapsed_time
359*800a58d9SAndroid Build Coastguard Worker        self._avd_type = avd_type
360*800a58d9SAndroid Build Coastguard Worker        self._avd_flavor = avd_flavor
361*800a58d9SAndroid Build Coastguard Worker        self._is_local = is_local  # True if this is a local instance
362*800a58d9SAndroid Build Coastguard Worker        self._device_information = device_information
363*800a58d9SAndroid Build Coastguard Worker        self._zone = zone
364*800a58d9SAndroid Build Coastguard Worker        self._autoconnect = self._GetAutoConnect()
365*800a58d9SAndroid Build Coastguard Worker
366*800a58d9SAndroid Build Coastguard Worker    def __repr__(self):
367*800a58d9SAndroid Build Coastguard Worker        """Return full name property for print."""
368*800a58d9SAndroid Build Coastguard Worker        return self._fullname
369*800a58d9SAndroid Build Coastguard Worker
370*800a58d9SAndroid Build Coastguard Worker    def Summary(self):
371*800a58d9SAndroid Build Coastguard Worker        """Let's make it easy to see what this class is holding."""
372*800a58d9SAndroid Build Coastguard Worker        representation = []
373*800a58d9SAndroid Build Coastguard Worker        representation.append(" name: %s" % self._name)
374*800a58d9SAndroid Build Coastguard Worker        representation.append("%s IP: %s" % (_INDENT, self._ip))
375*800a58d9SAndroid Build Coastguard Worker        representation.append("%s create time: %s" % (_INDENT, self._createtime))
376*800a58d9SAndroid Build Coastguard Worker        representation.append("%s elapse time: %s" % (_INDENT, self._elapsed_time))
377*800a58d9SAndroid Build Coastguard Worker        representation.append("%s status: %s" % (_INDENT, self._status))
378*800a58d9SAndroid Build Coastguard Worker        representation.append("%s avd type: %s" % (_INDENT, self._avd_type))
379*800a58d9SAndroid Build Coastguard Worker        representation.append("%s display: %s" % (_INDENT, self._display))
380*800a58d9SAndroid Build Coastguard Worker        representation.append("%s vnc: 127.0.0.1:%s" % (_INDENT, self._vnc_port))
381*800a58d9SAndroid Build Coastguard Worker        representation.append("%s zone: %s" % (_INDENT, self._zone))
382*800a58d9SAndroid Build Coastguard Worker        representation.append("%s autoconnect: %s" % (_INDENT, self._autoconnect))
383*800a58d9SAndroid Build Coastguard Worker        representation.append("%s webrtc port: %s" % (_INDENT, self._webrtc_port))
384*800a58d9SAndroid Build Coastguard Worker        representation.append("%s webrtc forward port: %s" %
385*800a58d9SAndroid Build Coastguard Worker                              (_INDENT, self._webrtc_forward_port))
386*800a58d9SAndroid Build Coastguard Worker
387*800a58d9SAndroid Build Coastguard Worker        if self._adb_port and self._device_information:
388*800a58d9SAndroid Build Coastguard Worker            serial_ip = self._ip if self._ip == "0.0.0.0" else "127.0.0.1"
389*800a58d9SAndroid Build Coastguard Worker            representation.append("%s adb serial: %s:%s" %
390*800a58d9SAndroid Build Coastguard Worker                                  (_INDENT, serial_ip, self._adb_port))
391*800a58d9SAndroid Build Coastguard Worker            representation.append("%s product: %s" % (
392*800a58d9SAndroid Build Coastguard Worker                _INDENT, self._device_information["product"]))
393*800a58d9SAndroid Build Coastguard Worker            representation.append("%s model: %s" % (
394*800a58d9SAndroid Build Coastguard Worker                _INDENT, self._device_information["model"]))
395*800a58d9SAndroid Build Coastguard Worker            representation.append("%s device: %s" % (
396*800a58d9SAndroid Build Coastguard Worker                _INDENT, self._device_information["device"]))
397*800a58d9SAndroid Build Coastguard Worker            representation.append("%s transport_id: %s" % (
398*800a58d9SAndroid Build Coastguard Worker                _INDENT, self._device_information["transport_id"]))
399*800a58d9SAndroid Build Coastguard Worker        else:
400*800a58d9SAndroid Build Coastguard Worker            representation.append("%s adb serial: disconnected" % _INDENT)
401*800a58d9SAndroid Build Coastguard Worker
402*800a58d9SAndroid Build Coastguard Worker        return "\n".join(representation)
403*800a58d9SAndroid Build Coastguard Worker
404*800a58d9SAndroid Build Coastguard Worker    def AdbConnected(self):
405*800a58d9SAndroid Build Coastguard Worker        """Check AVD adb connected.
406*800a58d9SAndroid Build Coastguard Worker
407*800a58d9SAndroid Build Coastguard Worker        Returns:
408*800a58d9SAndroid Build Coastguard Worker            Boolean, True when adb status of AVD is connected.
409*800a58d9SAndroid Build Coastguard Worker        """
410*800a58d9SAndroid Build Coastguard Worker        if self._adb_port and self._device_information:
411*800a58d9SAndroid Build Coastguard Worker            return True
412*800a58d9SAndroid Build Coastguard Worker        return False
413*800a58d9SAndroid Build Coastguard Worker
414*800a58d9SAndroid Build Coastguard Worker    def _GetAutoConnect(self):
415*800a58d9SAndroid Build Coastguard Worker        """Get the autoconnect of instance.
416*800a58d9SAndroid Build Coastguard Worker
417*800a58d9SAndroid Build Coastguard Worker        Returns:
418*800a58d9SAndroid Build Coastguard Worker            String of autoconnect type. None for no autoconnect.
419*800a58d9SAndroid Build Coastguard Worker        """
420*800a58d9SAndroid Build Coastguard Worker        if self._webrtc_port or self._webrtc_forward_port:
421*800a58d9SAndroid Build Coastguard Worker            return constants.INS_KEY_WEBRTC
422*800a58d9SAndroid Build Coastguard Worker        if self._vnc_port:
423*800a58d9SAndroid Build Coastguard Worker            return constants.INS_KEY_VNC
424*800a58d9SAndroid Build Coastguard Worker        if self._adb_port:
425*800a58d9SAndroid Build Coastguard Worker            return constants.INS_KEY_ADB
426*800a58d9SAndroid Build Coastguard Worker        return None
427*800a58d9SAndroid Build Coastguard Worker
428*800a58d9SAndroid Build Coastguard Worker    @property
429*800a58d9SAndroid Build Coastguard Worker    def name(self):
430*800a58d9SAndroid Build Coastguard Worker        """Return the instance name."""
431*800a58d9SAndroid Build Coastguard Worker        return self._name
432*800a58d9SAndroid Build Coastguard Worker
433*800a58d9SAndroid Build Coastguard Worker    @property
434*800a58d9SAndroid Build Coastguard Worker    def fullname(self):
435*800a58d9SAndroid Build Coastguard Worker        """Return the instance full name."""
436*800a58d9SAndroid Build Coastguard Worker        return self._fullname
437*800a58d9SAndroid Build Coastguard Worker
438*800a58d9SAndroid Build Coastguard Worker    @property
439*800a58d9SAndroid Build Coastguard Worker    def ip(self):
440*800a58d9SAndroid Build Coastguard Worker        """Return the ip."""
441*800a58d9SAndroid Build Coastguard Worker        return self._ip
442*800a58d9SAndroid Build Coastguard Worker
443*800a58d9SAndroid Build Coastguard Worker    @property
444*800a58d9SAndroid Build Coastguard Worker    def status(self):
445*800a58d9SAndroid Build Coastguard Worker        """Return status."""
446*800a58d9SAndroid Build Coastguard Worker        return self._status
447*800a58d9SAndroid Build Coastguard Worker
448*800a58d9SAndroid Build Coastguard Worker    @property
449*800a58d9SAndroid Build Coastguard Worker    def display(self):
450*800a58d9SAndroid Build Coastguard Worker        """Return display."""
451*800a58d9SAndroid Build Coastguard Worker        return self._display
452*800a58d9SAndroid Build Coastguard Worker
453*800a58d9SAndroid Build Coastguard Worker    @property
454*800a58d9SAndroid Build Coastguard Worker    def ssh_tunnel_is_connected(self):
455*800a58d9SAndroid Build Coastguard Worker        """Return the connect status."""
456*800a58d9SAndroid Build Coastguard Worker        return self._ssh_tunnel_is_connected
457*800a58d9SAndroid Build Coastguard Worker
458*800a58d9SAndroid Build Coastguard Worker    @property
459*800a58d9SAndroid Build Coastguard Worker    def createtime(self):
460*800a58d9SAndroid Build Coastguard Worker        """Return create time."""
461*800a58d9SAndroid Build Coastguard Worker        return self._createtime
462*800a58d9SAndroid Build Coastguard Worker
463*800a58d9SAndroid Build Coastguard Worker    @property
464*800a58d9SAndroid Build Coastguard Worker    def avd_type(self):
465*800a58d9SAndroid Build Coastguard Worker        """Return avd_type."""
466*800a58d9SAndroid Build Coastguard Worker        return self._avd_type
467*800a58d9SAndroid Build Coastguard Worker
468*800a58d9SAndroid Build Coastguard Worker    @property
469*800a58d9SAndroid Build Coastguard Worker    def avd_flavor(self):
470*800a58d9SAndroid Build Coastguard Worker        """Return avd_flavor."""
471*800a58d9SAndroid Build Coastguard Worker        return self._avd_flavor
472*800a58d9SAndroid Build Coastguard Worker
473*800a58d9SAndroid Build Coastguard Worker    @property
474*800a58d9SAndroid Build Coastguard Worker    def islocal(self):
475*800a58d9SAndroid Build Coastguard Worker        """Return if it is a local instance."""
476*800a58d9SAndroid Build Coastguard Worker        return self._is_local
477*800a58d9SAndroid Build Coastguard Worker
478*800a58d9SAndroid Build Coastguard Worker    @property
479*800a58d9SAndroid Build Coastguard Worker    def adb_port(self):
480*800a58d9SAndroid Build Coastguard Worker        """Return adb_port."""
481*800a58d9SAndroid Build Coastguard Worker        return self._adb_port
482*800a58d9SAndroid Build Coastguard Worker
483*800a58d9SAndroid Build Coastguard Worker    @property
484*800a58d9SAndroid Build Coastguard Worker    def vnc_port(self):
485*800a58d9SAndroid Build Coastguard Worker        """Return vnc_port."""
486*800a58d9SAndroid Build Coastguard Worker        return self._vnc_port
487*800a58d9SAndroid Build Coastguard Worker
488*800a58d9SAndroid Build Coastguard Worker    @property
489*800a58d9SAndroid Build Coastguard Worker    def webrtc_port(self):
490*800a58d9SAndroid Build Coastguard Worker        """Return webrtc_port."""
491*800a58d9SAndroid Build Coastguard Worker        return self._webrtc_port
492*800a58d9SAndroid Build Coastguard Worker
493*800a58d9SAndroid Build Coastguard Worker    @property
494*800a58d9SAndroid Build Coastguard Worker    def webrtc_forward_port(self):
495*800a58d9SAndroid Build Coastguard Worker        """Return webrtc_forward_port."""
496*800a58d9SAndroid Build Coastguard Worker        return self._webrtc_forward_port
497*800a58d9SAndroid Build Coastguard Worker
498*800a58d9SAndroid Build Coastguard Worker    @property
499*800a58d9SAndroid Build Coastguard Worker    def zone(self):
500*800a58d9SAndroid Build Coastguard Worker        """Return zone."""
501*800a58d9SAndroid Build Coastguard Worker        return self._zone
502*800a58d9SAndroid Build Coastguard Worker
503*800a58d9SAndroid Build Coastguard Worker    @property
504*800a58d9SAndroid Build Coastguard Worker    def autoconnect(self):
505*800a58d9SAndroid Build Coastguard Worker        """Return autoconnect."""
506*800a58d9SAndroid Build Coastguard Worker        return self._autoconnect
507*800a58d9SAndroid Build Coastguard Worker
508*800a58d9SAndroid Build Coastguard Worker
509*800a58d9SAndroid Build Coastguard Workerclass LocalInstance(Instance):
510*800a58d9SAndroid Build Coastguard Worker    """Class to store data of local cuttlefish instance."""
511*800a58d9SAndroid Build Coastguard Worker    def __init__(self, cf_config_path, ins_id=None):
512*800a58d9SAndroid Build Coastguard Worker        """Initialize a localInstance object.
513*800a58d9SAndroid Build Coastguard Worker
514*800a58d9SAndroid Build Coastguard Worker        Args:
515*800a58d9SAndroid Build Coastguard Worker            cf_config_path: String, path to the cf runtime config.
516*800a58d9SAndroid Build Coastguard Worker            ins_id: Integer, the id to specify the instance information.
517*800a58d9SAndroid Build Coastguard Worker        """
518*800a58d9SAndroid Build Coastguard Worker        self._cf_runtime_cfg = cvd_runtime_config.CvdRuntimeConfig(cf_config_path)
519*800a58d9SAndroid Build Coastguard Worker        self._instance_dir = self._cf_runtime_cfg.instance_dir
520*800a58d9SAndroid Build Coastguard Worker        self._virtual_disk_paths = self._cf_runtime_cfg.virtual_disk_paths
521*800a58d9SAndroid Build Coastguard Worker        self._local_instance_id = int(ins_id or self._cf_runtime_cfg.instance_id)
522*800a58d9SAndroid Build Coastguard Worker        self._instance_home = GetLocalInstanceHomeDir(self._local_instance_id)
523*800a58d9SAndroid Build Coastguard Worker        if self._cf_runtime_cfg.root_dir:
524*800a58d9SAndroid Build Coastguard Worker            self._instance_home = os.path.dirname(self._cf_runtime_cfg.root_dir)
525*800a58d9SAndroid Build Coastguard Worker
526*800a58d9SAndroid Build Coastguard Worker        ins_info = self._cf_runtime_cfg.instances.get(ins_id, {})
527*800a58d9SAndroid Build Coastguard Worker        adb_port = ins_info.get(_ADB_HOST_PORT) or self._cf_runtime_cfg.adb_port
528*800a58d9SAndroid Build Coastguard Worker        webrtc_device_id = (ins_info.get(constants.INS_KEY_WEBRTC_DEVICE_ID)
529*800a58d9SAndroid Build Coastguard Worker                            or f"cvd-{self._local_instance_id}")
530*800a58d9SAndroid Build Coastguard Worker        adb_serial = f"0.0.0.0:{adb_port}"
531*800a58d9SAndroid Build Coastguard Worker        display = []
532*800a58d9SAndroid Build Coastguard Worker        for display_config in self._cf_runtime_cfg.display_configs:
533*800a58d9SAndroid Build Coastguard Worker            display.append(_DISPLAY_STRING % {"x_res": display_config.get(_X_RES),
534*800a58d9SAndroid Build Coastguard Worker                                              "y_res": display_config.get(_Y_RES),
535*800a58d9SAndroid Build Coastguard Worker                                              "dpi": display_config.get(_DPI)})
536*800a58d9SAndroid Build Coastguard Worker        # TODO(143063678), there's no createtime info in
537*800a58d9SAndroid Build Coastguard Worker        # cuttlefish_config.json so far.
538*800a58d9SAndroid Build Coastguard Worker        webrtc_port = local_image_local_instance.LocalImageLocalInstance.GetWebrtcSigServerPort(
539*800a58d9SAndroid Build Coastguard Worker            self._local_instance_id)
540*800a58d9SAndroid Build Coastguard Worker        cvd_status_info = self._GetDevidInfoFromCvdStatus()
541*800a58d9SAndroid Build Coastguard Worker        if cvd_status_info:
542*800a58d9SAndroid Build Coastguard Worker            display = cvd_status_info.get(_DISPLAYS)
543*800a58d9SAndroid Build Coastguard Worker            webrtc_port = int(cvd_status_info.get(_WEBRTC_PORT))
544*800a58d9SAndroid Build Coastguard Worker            adb_serial = cvd_status_info.get(_ADB_SERIAL)
545*800a58d9SAndroid Build Coastguard Worker
546*800a58d9SAndroid Build Coastguard Worker        name = GetLocalInstanceName(self._local_instance_id)
547*800a58d9SAndroid Build Coastguard Worker        fullname = _GetDeviceFullName(adb_serial, name, None, webrtc_device_id)
548*800a58d9SAndroid Build Coastguard Worker        adb_device = AdbTools(device_serial=adb_serial)
549*800a58d9SAndroid Build Coastguard Worker        device_information = None
550*800a58d9SAndroid Build Coastguard Worker        if adb_device.IsAdbConnected():
551*800a58d9SAndroid Build Coastguard Worker            device_information = adb_device.device_information
552*800a58d9SAndroid Build Coastguard Worker
553*800a58d9SAndroid Build Coastguard Worker        super().__init__(
554*800a58d9SAndroid Build Coastguard Worker            name=name, fullname=fullname, display=display, ip="0.0.0.0",
555*800a58d9SAndroid Build Coastguard Worker            status=constants.INS_STATUS_RUNNING,
556*800a58d9SAndroid Build Coastguard Worker            adb_port=adb_port,
557*800a58d9SAndroid Build Coastguard Worker            vnc_port=self._cf_runtime_cfg.vnc_port,
558*800a58d9SAndroid Build Coastguard Worker            createtime=None, elapsed_time=None, avd_type=constants.TYPE_CF,
559*800a58d9SAndroid Build Coastguard Worker            is_local=True, device_information=device_information,
560*800a58d9SAndroid Build Coastguard Worker            zone=_LOCAL_ZONE, webrtc_port=webrtc_port)
561*800a58d9SAndroid Build Coastguard Worker
562*800a58d9SAndroid Build Coastguard Worker    def Summary(self):
563*800a58d9SAndroid Build Coastguard Worker        """Return the string that this class is holding."""
564*800a58d9SAndroid Build Coastguard Worker        instance_home = "%s instance home: %s" % (_INDENT, self._instance_dir)
565*800a58d9SAndroid Build Coastguard Worker        return "%s\n%s" % (super().Summary(), instance_home)
566*800a58d9SAndroid Build Coastguard Worker
567*800a58d9SAndroid Build Coastguard Worker    def _GetCvdEnv(self):
568*800a58d9SAndroid Build Coastguard Worker        """Get the environment to run cvd commands.
569*800a58d9SAndroid Build Coastguard Worker
570*800a58d9SAndroid Build Coastguard Worker        Returns:
571*800a58d9SAndroid Build Coastguard Worker            os.environ with cuttlefish variables updated.
572*800a58d9SAndroid Build Coastguard Worker        """
573*800a58d9SAndroid Build Coastguard Worker        cvd_env = os.environ.copy()
574*800a58d9SAndroid Build Coastguard Worker        cvd_env[constants.ENV_ANDROID_HOST_OUT] = os.path.dirname(
575*800a58d9SAndroid Build Coastguard Worker            self._cf_runtime_cfg.cvd_tools_path)
576*800a58d9SAndroid Build Coastguard Worker        cvd_env[constants.ENV_ANDROID_SOONG_HOST_OUT] = os.path.dirname(
577*800a58d9SAndroid Build Coastguard Worker            self._cf_runtime_cfg.cvd_tools_path)
578*800a58d9SAndroid Build Coastguard Worker        cvd_env[constants.ENV_CUTTLEFISH_CONFIG_FILE] = self._cf_runtime_cfg.config_path
579*800a58d9SAndroid Build Coastguard Worker        cvd_env[constants.ENV_CVD_HOME] = self._instance_home
580*800a58d9SAndroid Build Coastguard Worker        cvd_env[constants.ENV_CUTTLEFISH_INSTANCE] = str(self._local_instance_id)
581*800a58d9SAndroid Build Coastguard Worker        return cvd_env
582*800a58d9SAndroid Build Coastguard Worker
583*800a58d9SAndroid Build Coastguard Worker    def _GetDevidInfoFromCvdStatus(self):
584*800a58d9SAndroid Build Coastguard Worker        """Get device information from 'cvd status'.
585*800a58d9SAndroid Build Coastguard Worker
586*800a58d9SAndroid Build Coastguard Worker        Execute 'cvd status --print -instance_name=name' cmd to get devices
587*800a58d9SAndroid Build Coastguard Worker        information.
588*800a58d9SAndroid Build Coastguard Worker
589*800a58d9SAndroid Build Coastguard Worker        Returns
590*800a58d9SAndroid Build Coastguard Worker            Output of 'cvd status'. None for fail to run 'cvd status'.
591*800a58d9SAndroid Build Coastguard Worker        """
592*800a58d9SAndroid Build Coastguard Worker        try:
593*800a58d9SAndroid Build Coastguard Worker            cvd_tool = os.path.join(self._instance_home, _CVD_BIN_FOLDER, _CVD_BIN)
594*800a58d9SAndroid Build Coastguard Worker            ins_name = f"cvd-{self._local_instance_id}"
595*800a58d9SAndroid Build Coastguard Worker            cvd_status_cmd = f"{cvd_tool} status -print -instance_name={ins_name}"
596*800a58d9SAndroid Build Coastguard Worker            if not os.path.exists(cvd_tool):
597*800a58d9SAndroid Build Coastguard Worker                logger.warning("Cvd tools path doesn't exist:%s", cvd_tool)
598*800a58d9SAndroid Build Coastguard Worker                return None
599*800a58d9SAndroid Build Coastguard Worker            logger.debug("Running cmd [%s] to get device info.", cvd_status_cmd)
600*800a58d9SAndroid Build Coastguard Worker            process = subprocess.Popen(cvd_status_cmd, shell=True, text=True,
601*800a58d9SAndroid Build Coastguard Worker                                       env=self._GetCvdEnv(),
602*800a58d9SAndroid Build Coastguard Worker                                       stdout=subprocess.PIPE,
603*800a58d9SAndroid Build Coastguard Worker                                       stderr=subprocess.PIPE)
604*800a58d9SAndroid Build Coastguard Worker            stdout, _ = process.communicate(timeout=_CVD_TIMEOUT)
605*800a58d9SAndroid Build Coastguard Worker            logger.debug("Output of cvd status: %s", stdout)
606*800a58d9SAndroid Build Coastguard Worker            return json.loads(self._ParsingCvdFleetOutput(stdout))
607*800a58d9SAndroid Build Coastguard Worker        except (subprocess.CalledProcessError, subprocess.TimeoutExpired,
608*800a58d9SAndroid Build Coastguard Worker                json.JSONDecodeError) as error:
609*800a58d9SAndroid Build Coastguard Worker            logger.error("Failed to run 'cvd status': %s", str(error))
610*800a58d9SAndroid Build Coastguard Worker        return None
611*800a58d9SAndroid Build Coastguard Worker
612*800a58d9SAndroid Build Coastguard Worker    @staticmethod
613*800a58d9SAndroid Build Coastguard Worker    def _ParsingCvdFleetOutput(output):
614*800a58d9SAndroid Build Coastguard Worker        """Parsing the output of cvd fleet.
615*800a58d9SAndroid Build Coastguard Worker
616*800a58d9SAndroid Build Coastguard Worker        The output example:
617*800a58d9SAndroid Build Coastguard Worker            WARNING: cvd_server client version (8245608) does not match.
618*800a58d9SAndroid Build Coastguard Worker            {
619*800a58d9SAndroid Build Coastguard Worker                "adb_serial" : "0.0.0.0:6520",
620*800a58d9SAndroid Build Coastguard Worker                "assembly_dir" : "/home/cuttlefish_runtime/assembly",
621*800a58d9SAndroid Build Coastguard Worker                "displays" : ["720 x 1280 ( 320 )"],
622*800a58d9SAndroid Build Coastguard Worker                "instance_dir" : "/home/cuttlefish_runtime/instances/cvd-1",
623*800a58d9SAndroid Build Coastguard Worker                "instance_name" : "cvd-1",
624*800a58d9SAndroid Build Coastguard Worker                "status" : "Running",
625*800a58d9SAndroid Build Coastguard Worker                "web_access" : "https://0.0.0.0:8443/client.html?deviceId=cvd-1",
626*800a58d9SAndroid Build Coastguard Worker                "webrtc_port" : "8443"
627*800a58d9SAndroid Build Coastguard Worker            }
628*800a58d9SAndroid Build Coastguard Worker
629*800a58d9SAndroid Build Coastguard Worker        Returns:
630*800a58d9SAndroid Build Coastguard Worker            Parsed output filtered warning message.
631*800a58d9SAndroid Build Coastguard Worker        """
632*800a58d9SAndroid Build Coastguard Worker        device_match = _RE_DEVICE_INFO.match(output)
633*800a58d9SAndroid Build Coastguard Worker        if device_match:
634*800a58d9SAndroid Build Coastguard Worker            return device_match.group("device_info")
635*800a58d9SAndroid Build Coastguard Worker        return ""
636*800a58d9SAndroid Build Coastguard Worker
637*800a58d9SAndroid Build Coastguard Worker    def CvdStatus(self):
638*800a58d9SAndroid Build Coastguard Worker        """check if local instance is active.
639*800a58d9SAndroid Build Coastguard Worker
640*800a58d9SAndroid Build Coastguard Worker        Execute cvd_status cmd to check if it exit without error.
641*800a58d9SAndroid Build Coastguard Worker
642*800a58d9SAndroid Build Coastguard Worker        Returns
643*800a58d9SAndroid Build Coastguard Worker            True if instance is active.
644*800a58d9SAndroid Build Coastguard Worker        """
645*800a58d9SAndroid Build Coastguard Worker        if not self._cf_runtime_cfg.cvd_tools_path:
646*800a58d9SAndroid Build Coastguard Worker            logger.debug("No cvd tools path found from config:%s",
647*800a58d9SAndroid Build Coastguard Worker                         self._cf_runtime_cfg.config_path)
648*800a58d9SAndroid Build Coastguard Worker            return False
649*800a58d9SAndroid Build Coastguard Worker        try:
650*800a58d9SAndroid Build Coastguard Worker            cvd_status_cmd = os.path.join(self._cf_runtime_cfg.cvd_tools_path,
651*800a58d9SAndroid Build Coastguard Worker                                          _CVD_STATUS_BIN)
652*800a58d9SAndroid Build Coastguard Worker            # TODO(b/150575261): Change the cvd home and cvd artifact path to
653*800a58d9SAndroid Build Coastguard Worker            #  another place instead of /tmp to prevent from the file not
654*800a58d9SAndroid Build Coastguard Worker            #  found exception.
655*800a58d9SAndroid Build Coastguard Worker            if not os.path.exists(cvd_status_cmd):
656*800a58d9SAndroid Build Coastguard Worker                logger.warning("Cvd tools path doesn't exist:%s", cvd_status_cmd)
657*800a58d9SAndroid Build Coastguard Worker                for env_host_out in [constants.ENV_ANDROID_SOONG_HOST_OUT,
658*800a58d9SAndroid Build Coastguard Worker                                     constants.ENV_ANDROID_HOST_OUT]:
659*800a58d9SAndroid Build Coastguard Worker                    if os.environ.get(env_host_out, _NO_ANDROID_ENV) in cvd_status_cmd:
660*800a58d9SAndroid Build Coastguard Worker                        logger.warning(
661*800a58d9SAndroid Build Coastguard Worker                            "Can't find the cvd_status tool (Try lunching a "
662*800a58d9SAndroid Build Coastguard Worker                            "cuttlefish target like aosp_cf_x86_64_phone-userdebug "
663*800a58d9SAndroid Build Coastguard Worker                            "and running 'make hosttar' before list/delete local "
664*800a58d9SAndroid Build Coastguard Worker                            "instances)")
665*800a58d9SAndroid Build Coastguard Worker                return False
666*800a58d9SAndroid Build Coastguard Worker            logger.debug("Running cmd[%s] to check cvd status.", cvd_status_cmd)
667*800a58d9SAndroid Build Coastguard Worker            process = subprocess.Popen(cvd_status_cmd,
668*800a58d9SAndroid Build Coastguard Worker                                       stdin=None,
669*800a58d9SAndroid Build Coastguard Worker                                       stdout=subprocess.PIPE,
670*800a58d9SAndroid Build Coastguard Worker                                       stderr=subprocess.STDOUT,
671*800a58d9SAndroid Build Coastguard Worker                                       env=self._GetCvdEnv())
672*800a58d9SAndroid Build Coastguard Worker            stdout, _ = process.communicate()
673*800a58d9SAndroid Build Coastguard Worker            if process.returncode != 0:
674*800a58d9SAndroid Build Coastguard Worker                if stdout:
675*800a58d9SAndroid Build Coastguard Worker                    logger.debug("Local instance[%s] is not active: %s",
676*800a58d9SAndroid Build Coastguard Worker                                 self.name, stdout.strip())
677*800a58d9SAndroid Build Coastguard Worker                return False
678*800a58d9SAndroid Build Coastguard Worker            return True
679*800a58d9SAndroid Build Coastguard Worker        except subprocess.CalledProcessError as cpe:
680*800a58d9SAndroid Build Coastguard Worker            logger.error("Failed to run cvd_status: %s", cpe.output)
681*800a58d9SAndroid Build Coastguard Worker            return False
682*800a58d9SAndroid Build Coastguard Worker
683*800a58d9SAndroid Build Coastguard Worker    def Delete(self):
684*800a58d9SAndroid Build Coastguard Worker        """Execute "cvd stop" to stop local cuttlefish instance.
685*800a58d9SAndroid Build Coastguard Worker
686*800a58d9SAndroid Build Coastguard Worker        - We should get the same host tool used to delete instance.
687*800a58d9SAndroid Build Coastguard Worker        - Add CUTTLEFISH_CONFIG_FILE env variable to tell cvd which cvd need to
688*800a58d9SAndroid Build Coastguard Worker          be deleted.
689*800a58d9SAndroid Build Coastguard Worker        - Stop adb since local instance use the fixed adb port and could be
690*800a58d9SAndroid Build Coastguard Worker         reused again soon.
691*800a58d9SAndroid Build Coastguard Worker        """
692*800a58d9SAndroid Build Coastguard Worker        ins_home_dir = GetLocalInstanceHomeDir(self._local_instance_id)
693*800a58d9SAndroid Build Coastguard Worker        cvd_tool = os.path.join(ins_home_dir, _CVD_BIN_FOLDER, _CVD_BIN)
694*800a58d9SAndroid Build Coastguard Worker        stop_cvd_cmd = f"{cvd_tool} stop"
695*800a58d9SAndroid Build Coastguard Worker        logger.debug("Running cmd[%s] to delete local cvd", stop_cvd_cmd)
696*800a58d9SAndroid Build Coastguard Worker        if not self.instance_dir:
697*800a58d9SAndroid Build Coastguard Worker            logger.error("instance_dir is null!! instance[%d] might not be"
698*800a58d9SAndroid Build Coastguard Worker                         " deleted", self._local_instance_id)
699*800a58d9SAndroid Build Coastguard Worker        try:
700*800a58d9SAndroid Build Coastguard Worker            output = subprocess.check_output(
701*800a58d9SAndroid Build Coastguard Worker                utils.AddUserGroupsToCmd(stop_cvd_cmd,
702*800a58d9SAndroid Build Coastguard Worker                                         constants.LIST_CF_USER_GROUPS),
703*800a58d9SAndroid Build Coastguard Worker                stderr=subprocess.STDOUT, shell=True, env=self._GetCvdEnv(),
704*800a58d9SAndroid Build Coastguard Worker                text=True, timeout=_CVD_TIMEOUT)
705*800a58d9SAndroid Build Coastguard Worker            # TODO: Remove workaround of stop_cvd when 'cvd stop' is stable.
706*800a58d9SAndroid Build Coastguard Worker            if _CVD_STOP_ERROR_KEYWORDS in output:
707*800a58d9SAndroid Build Coastguard Worker                logger.debug("Fail to stop cvd: %s", output)
708*800a58d9SAndroid Build Coastguard Worker                self._ExecuteStopCvd(os.path.join(ins_home_dir, _CVD_BIN_FOLDER))
709*800a58d9SAndroid Build Coastguard Worker        except (subprocess.TimeoutExpired, subprocess.CalledProcessError) as e:
710*800a58d9SAndroid Build Coastguard Worker            logger.debug("'cvd stop' error: %s", str(e))
711*800a58d9SAndroid Build Coastguard Worker            self._ExecuteStopCvd(os.path.join(ins_home_dir, _CVD_BIN_FOLDER))
712*800a58d9SAndroid Build Coastguard Worker
713*800a58d9SAndroid Build Coastguard Worker        adb_cmd = AdbTools(self.adb_port)
714*800a58d9SAndroid Build Coastguard Worker        # When relaunch a local instance, we need to pass in retry=True to make
715*800a58d9SAndroid Build Coastguard Worker        # sure adb device is completely gone since it will use the same adb port
716*800a58d9SAndroid Build Coastguard Worker        adb_cmd.DisconnectAdb(retry=True)
717*800a58d9SAndroid Build Coastguard Worker
718*800a58d9SAndroid Build Coastguard Worker    def _ExecuteStopCvd(self, dir_path):
719*800a58d9SAndroid Build Coastguard Worker        """Execute "stop_cvd" to stop local cuttlefish instance.
720*800a58d9SAndroid Build Coastguard Worker
721*800a58d9SAndroid Build Coastguard Worker        Args:
722*800a58d9SAndroid Build Coastguard Worker            bin_dir: String, directory path of "stop_cvd".
723*800a58d9SAndroid Build Coastguard Worker        """
724*800a58d9SAndroid Build Coastguard Worker        stop_cvd_cmd = os.path.join(dir_path, constants.CMD_STOP_CVD)
725*800a58d9SAndroid Build Coastguard Worker        subprocess.check_call(
726*800a58d9SAndroid Build Coastguard Worker            utils.AddUserGroupsToCmd(
727*800a58d9SAndroid Build Coastguard Worker                stop_cvd_cmd, constants.LIST_CF_USER_GROUPS),
728*800a58d9SAndroid Build Coastguard Worker            stderr=subprocess.STDOUT, shell=True, env=self._GetCvdEnv())
729*800a58d9SAndroid Build Coastguard Worker
730*800a58d9SAndroid Build Coastguard Worker    def GetLock(self):
731*800a58d9SAndroid Build Coastguard Worker        """Return the LocalInstanceLock for this object."""
732*800a58d9SAndroid Build Coastguard Worker        return GetLocalInstanceLock(self._local_instance_id)
733*800a58d9SAndroid Build Coastguard Worker
734*800a58d9SAndroid Build Coastguard Worker    @property
735*800a58d9SAndroid Build Coastguard Worker    def instance_dir(self):
736*800a58d9SAndroid Build Coastguard Worker        """Return _instance_dir."""
737*800a58d9SAndroid Build Coastguard Worker        return self._instance_dir
738*800a58d9SAndroid Build Coastguard Worker
739*800a58d9SAndroid Build Coastguard Worker    @property
740*800a58d9SAndroid Build Coastguard Worker    def instance_id(self):
741*800a58d9SAndroid Build Coastguard Worker        """Return _local_instance_id."""
742*800a58d9SAndroid Build Coastguard Worker        return self._local_instance_id
743*800a58d9SAndroid Build Coastguard Worker
744*800a58d9SAndroid Build Coastguard Worker    @property
745*800a58d9SAndroid Build Coastguard Worker    def virtual_disk_paths(self):
746*800a58d9SAndroid Build Coastguard Worker        """Return virtual_disk_paths"""
747*800a58d9SAndroid Build Coastguard Worker        return self._virtual_disk_paths
748*800a58d9SAndroid Build Coastguard Worker
749*800a58d9SAndroid Build Coastguard Worker    @property
750*800a58d9SAndroid Build Coastguard Worker    def cf_runtime_cfg(self):
751*800a58d9SAndroid Build Coastguard Worker        """Return _cf_runtime_cfg"""
752*800a58d9SAndroid Build Coastguard Worker        return self._cf_runtime_cfg
753*800a58d9SAndroid Build Coastguard Worker
754*800a58d9SAndroid Build Coastguard Worker
755*800a58d9SAndroid Build Coastguard Workerclass LocalGoldfishInstance(Instance):
756*800a58d9SAndroid Build Coastguard Worker    """Class to store data of local goldfish instance.
757*800a58d9SAndroid Build Coastguard Worker
758*800a58d9SAndroid Build Coastguard Worker    A goldfish instance binds to a console port and an adb port. The console
759*800a58d9SAndroid Build Coastguard Worker    port is for `adb emu` to send emulator-specific commands. The adb port is
760*800a58d9SAndroid Build Coastguard Worker    for `adb connect` to start a TCP connection. By convention, the console
761*800a58d9SAndroid Build Coastguard Worker    port is an even number, and the adb port is the console port + 1. The first
762*800a58d9SAndroid Build Coastguard Worker    instance uses port 5554 and 5555, the second instance uses 5556 and 5557,
763*800a58d9SAndroid Build Coastguard Worker    and so on.
764*800a58d9SAndroid Build Coastguard Worker    """
765*800a58d9SAndroid Build Coastguard Worker
766*800a58d9SAndroid Build Coastguard Worker    _INSTANCE_NAME_PATTERN = re.compile(
767*800a58d9SAndroid Build Coastguard Worker        r"^local-goldfish-instance-(?P<id>\d+)$")
768*800a58d9SAndroid Build Coastguard Worker    _INSTANCE_NAME_FORMAT = "local-goldfish-instance-%(id)s"
769*800a58d9SAndroid Build Coastguard Worker    _EMULATOR_DEFAULT_CONSOLE_PORT = 5554
770*800a58d9SAndroid Build Coastguard Worker    _DEFAULT_ADB_LOCAL_TRANSPORT_MAX_PORT = 5585
771*800a58d9SAndroid Build Coastguard Worker    _DEVICE_SERIAL_FORMAT = "emulator-%(console_port)s"
772*800a58d9SAndroid Build Coastguard Worker    _DEVICE_SERIAL_PATTERN = re.compile(r"^emulator-(?P<console_port>\d+)$")
773*800a58d9SAndroid Build Coastguard Worker
774*800a58d9SAndroid Build Coastguard Worker    def __init__(self, local_instance_id, avd_flavor=None, create_time=None,
775*800a58d9SAndroid Build Coastguard Worker                 x_res=None, y_res=None, dpi=None):
776*800a58d9SAndroid Build Coastguard Worker        """Initialize a LocalGoldfishInstance object.
777*800a58d9SAndroid Build Coastguard Worker
778*800a58d9SAndroid Build Coastguard Worker        Args:
779*800a58d9SAndroid Build Coastguard Worker            local_instance_id: Integer of instance id.
780*800a58d9SAndroid Build Coastguard Worker            avd_flavor: String, the flavor of the virtual device.
781*800a58d9SAndroid Build Coastguard Worker            create_time: String, the creation date and time.
782*800a58d9SAndroid Build Coastguard Worker            x_res: Integer of x dimension.
783*800a58d9SAndroid Build Coastguard Worker            y_res: Integer of y dimension.
784*800a58d9SAndroid Build Coastguard Worker            dpi: Integer of dpi.
785*800a58d9SAndroid Build Coastguard Worker        """
786*800a58d9SAndroid Build Coastguard Worker        self._id = local_instance_id
787*800a58d9SAndroid Build Coastguard Worker        adb_port = self.console_port + 1
788*800a58d9SAndroid Build Coastguard Worker        self._adb = AdbTools(adb_port=adb_port,
789*800a58d9SAndroid Build Coastguard Worker                             device_serial=self.device_serial)
790*800a58d9SAndroid Build Coastguard Worker
791*800a58d9SAndroid Build Coastguard Worker        name = self._INSTANCE_NAME_FORMAT % {"id": local_instance_id}
792*800a58d9SAndroid Build Coastguard Worker
793*800a58d9SAndroid Build Coastguard Worker        elapsed_time = _GetElapsedTime(create_time) if create_time else None
794*800a58d9SAndroid Build Coastguard Worker
795*800a58d9SAndroid Build Coastguard Worker        fullname = _GetDeviceFullName(self.device_serial, name, elapsed_time)
796*800a58d9SAndroid Build Coastguard Worker
797*800a58d9SAndroid Build Coastguard Worker        if x_res and y_res and dpi:
798*800a58d9SAndroid Build Coastguard Worker            display = _DISPLAY_STRING % {"x_res": x_res, "y_res": y_res,
799*800a58d9SAndroid Build Coastguard Worker                                         "dpi": dpi}
800*800a58d9SAndroid Build Coastguard Worker        else:
801*800a58d9SAndroid Build Coastguard Worker            display = "unknown"
802*800a58d9SAndroid Build Coastguard Worker
803*800a58d9SAndroid Build Coastguard Worker        device_information = (self._adb.device_information if
804*800a58d9SAndroid Build Coastguard Worker                              self._adb.device_information else None)
805*800a58d9SAndroid Build Coastguard Worker
806*800a58d9SAndroid Build Coastguard Worker        super().__init__(
807*800a58d9SAndroid Build Coastguard Worker            name=name, fullname=fullname, display=display, ip="127.0.0.1",
808*800a58d9SAndroid Build Coastguard Worker            status=None, adb_port=adb_port, avd_type=constants.TYPE_GF,
809*800a58d9SAndroid Build Coastguard Worker            createtime=create_time, elapsed_time=elapsed_time,
810*800a58d9SAndroid Build Coastguard Worker            avd_flavor=avd_flavor, is_local=True,
811*800a58d9SAndroid Build Coastguard Worker            device_information=device_information)
812*800a58d9SAndroid Build Coastguard Worker
813*800a58d9SAndroid Build Coastguard Worker    @staticmethod
814*800a58d9SAndroid Build Coastguard Worker    def _GetInstanceDirRoot():
815*800a58d9SAndroid Build Coastguard Worker        """Return the root directory of all instance directories."""
816*800a58d9SAndroid Build Coastguard Worker        return os.path.join(tempfile.gettempdir(), "acloud_gf_temp")
817*800a58d9SAndroid Build Coastguard Worker
818*800a58d9SAndroid Build Coastguard Worker    @property
819*800a58d9SAndroid Build Coastguard Worker    def adb(self):
820*800a58d9SAndroid Build Coastguard Worker        """Return the AdbTools to send emulator commands to this instance."""
821*800a58d9SAndroid Build Coastguard Worker        return self._adb
822*800a58d9SAndroid Build Coastguard Worker
823*800a58d9SAndroid Build Coastguard Worker    @property
824*800a58d9SAndroid Build Coastguard Worker    def console_port(self):
825*800a58d9SAndroid Build Coastguard Worker        """Return the console port as an integer."""
826*800a58d9SAndroid Build Coastguard Worker        # Emulator requires the console port to be an even number.
827*800a58d9SAndroid Build Coastguard Worker        return self._EMULATOR_DEFAULT_CONSOLE_PORT + (self._id - 1) * 2
828*800a58d9SAndroid Build Coastguard Worker
829*800a58d9SAndroid Build Coastguard Worker    @property
830*800a58d9SAndroid Build Coastguard Worker    def device_serial(self):
831*800a58d9SAndroid Build Coastguard Worker        """Return the serial number that contains the console port."""
832*800a58d9SAndroid Build Coastguard Worker        return self._DEVICE_SERIAL_FORMAT % {"console_port": self.console_port}
833*800a58d9SAndroid Build Coastguard Worker
834*800a58d9SAndroid Build Coastguard Worker    @property
835*800a58d9SAndroid Build Coastguard Worker    def instance_dir(self):
836*800a58d9SAndroid Build Coastguard Worker        """Return the path to instance directory."""
837*800a58d9SAndroid Build Coastguard Worker        return os.path.join(self._GetInstanceDirRoot(),
838*800a58d9SAndroid Build Coastguard Worker                            self._INSTANCE_NAME_FORMAT % {"id": self._id})
839*800a58d9SAndroid Build Coastguard Worker
840*800a58d9SAndroid Build Coastguard Worker    @classmethod
841*800a58d9SAndroid Build Coastguard Worker    def GetIdByName(cls, name):
842*800a58d9SAndroid Build Coastguard Worker        """Get id by name.
843*800a58d9SAndroid Build Coastguard Worker
844*800a58d9SAndroid Build Coastguard Worker        Args:
845*800a58d9SAndroid Build Coastguard Worker            name: String of instance name.
846*800a58d9SAndroid Build Coastguard Worker
847*800a58d9SAndroid Build Coastguard Worker        Return:
848*800a58d9SAndroid Build Coastguard Worker            The instance id as an integer if the name is in valid format.
849*800a58d9SAndroid Build Coastguard Worker            None if the name does not represent a local goldfish instance.
850*800a58d9SAndroid Build Coastguard Worker        """
851*800a58d9SAndroid Build Coastguard Worker        match = cls._INSTANCE_NAME_PATTERN.match(name)
852*800a58d9SAndroid Build Coastguard Worker        if match:
853*800a58d9SAndroid Build Coastguard Worker            return int(match.group("id"))
854*800a58d9SAndroid Build Coastguard Worker        return None
855*800a58d9SAndroid Build Coastguard Worker
856*800a58d9SAndroid Build Coastguard Worker    @classmethod
857*800a58d9SAndroid Build Coastguard Worker    def GetLockById(cls, instance_id):
858*800a58d9SAndroid Build Coastguard Worker        """Get LocalInstanceLock by id."""
859*800a58d9SAndroid Build Coastguard Worker        lock_path = os.path.join(
860*800a58d9SAndroid Build Coastguard Worker            cls._GetInstanceDirRoot(),
861*800a58d9SAndroid Build Coastguard Worker            (cls._INSTANCE_NAME_FORMAT % {"id": instance_id}) + ".lock")
862*800a58d9SAndroid Build Coastguard Worker        return LocalInstanceLock(lock_path)
863*800a58d9SAndroid Build Coastguard Worker
864*800a58d9SAndroid Build Coastguard Worker    def GetLock(self):
865*800a58d9SAndroid Build Coastguard Worker        """Return the LocalInstanceLock for this object."""
866*800a58d9SAndroid Build Coastguard Worker        return self.GetLockById(self._id)
867*800a58d9SAndroid Build Coastguard Worker
868*800a58d9SAndroid Build Coastguard Worker    @classmethod
869*800a58d9SAndroid Build Coastguard Worker    def GetExistingInstances(cls):
870*800a58d9SAndroid Build Coastguard Worker        """Get the list of instances that adb can send emu commands to."""
871*800a58d9SAndroid Build Coastguard Worker        instances = []
872*800a58d9SAndroid Build Coastguard Worker        for serial in AdbTools.GetDeviceSerials():
873*800a58d9SAndroid Build Coastguard Worker            match = cls._DEVICE_SERIAL_PATTERN.match(serial)
874*800a58d9SAndroid Build Coastguard Worker            if not match:
875*800a58d9SAndroid Build Coastguard Worker                continue
876*800a58d9SAndroid Build Coastguard Worker            port = int(match.group("console_port"))
877*800a58d9SAndroid Build Coastguard Worker            instance_id = (port - cls._EMULATOR_DEFAULT_CONSOLE_PORT) // 2 + 1
878*800a58d9SAndroid Build Coastguard Worker            instances.append(LocalGoldfishInstance(instance_id))
879*800a58d9SAndroid Build Coastguard Worker        return instances
880*800a58d9SAndroid Build Coastguard Worker
881*800a58d9SAndroid Build Coastguard Worker    @classmethod
882*800a58d9SAndroid Build Coastguard Worker    def GetMaxNumberOfInstances(cls):
883*800a58d9SAndroid Build Coastguard Worker        """Get number of emulators that adb can detect."""
884*800a58d9SAndroid Build Coastguard Worker        max_port = os.environ.get("ADB_LOCAL_TRANSPORT_MAX_PORT",
885*800a58d9SAndroid Build Coastguard Worker                                  cls._DEFAULT_ADB_LOCAL_TRANSPORT_MAX_PORT)
886*800a58d9SAndroid Build Coastguard Worker        try:
887*800a58d9SAndroid Build Coastguard Worker            max_port = int(max_port)
888*800a58d9SAndroid Build Coastguard Worker        except ValueError:
889*800a58d9SAndroid Build Coastguard Worker            max_port = cls._DEFAULT_ADB_LOCAL_TRANSPORT_MAX_PORT
890*800a58d9SAndroid Build Coastguard Worker        if (max_port < cls._EMULATOR_DEFAULT_CONSOLE_PORT or
891*800a58d9SAndroid Build Coastguard Worker                max_port > constants.MAX_PORT):
892*800a58d9SAndroid Build Coastguard Worker            max_port = cls._DEFAULT_ADB_LOCAL_TRANSPORT_MAX_PORT
893*800a58d9SAndroid Build Coastguard Worker        return (max_port + 1 - cls._EMULATOR_DEFAULT_CONSOLE_PORT) // 2
894*800a58d9SAndroid Build Coastguard Worker
895*800a58d9SAndroid Build Coastguard Worker
896*800a58d9SAndroid Build Coastguard Workerclass RemoteInstance(Instance):
897*800a58d9SAndroid Build Coastguard Worker    """Class to store data of remote instance."""
898*800a58d9SAndroid Build Coastguard Worker
899*800a58d9SAndroid Build Coastguard Worker    # pylint: disable=too-many-locals
900*800a58d9SAndroid Build Coastguard Worker    def __init__(self, gce_instance):
901*800a58d9SAndroid Build Coastguard Worker        """Process the args into class vars.
902*800a58d9SAndroid Build Coastguard Worker
903*800a58d9SAndroid Build Coastguard Worker        RemoteInstace initialized by gce dict object. We parse the required data
904*800a58d9SAndroid Build Coastguard Worker        from gce_instance to local variables.
905*800a58d9SAndroid Build Coastguard Worker        Reference:
906*800a58d9SAndroid Build Coastguard Worker        https://cloud.google.com/compute/docs/reference/rest/v1/instances/get
907*800a58d9SAndroid Build Coastguard Worker
908*800a58d9SAndroid Build Coastguard Worker        We also gather more details on client side including the forwarding adb
909*800a58d9SAndroid Build Coastguard Worker        port and vnc port which will be used to determine the status of ssh
910*800a58d9SAndroid Build Coastguard Worker        tunnel connection.
911*800a58d9SAndroid Build Coastguard Worker
912*800a58d9SAndroid Build Coastguard Worker        The status of gce instance will be displayed in _fullname property:
913*800a58d9SAndroid Build Coastguard Worker        - Connected: If gce instance and ssh tunnel and adb connection are all
914*800a58d9SAndroid Build Coastguard Worker         active.
915*800a58d9SAndroid Build Coastguard Worker        - No connected: If ssh tunnel or adb connection is not found.
916*800a58d9SAndroid Build Coastguard Worker        - Terminated: If we can't retrieve the public ip from gce instance.
917*800a58d9SAndroid Build Coastguard Worker
918*800a58d9SAndroid Build Coastguard Worker        Args:
919*800a58d9SAndroid Build Coastguard Worker            gce_instance: dict object queried from gce.
920*800a58d9SAndroid Build Coastguard Worker        """
921*800a58d9SAndroid Build Coastguard Worker        name = gce_instance.get(constants.INS_KEY_NAME)
922*800a58d9SAndroid Build Coastguard Worker
923*800a58d9SAndroid Build Coastguard Worker        create_time = gce_instance.get(constants.INS_KEY_CREATETIME)
924*800a58d9SAndroid Build Coastguard Worker        elapsed_time = _GetElapsedTime(create_time)
925*800a58d9SAndroid Build Coastguard Worker        status = gce_instance.get(constants.INS_KEY_STATUS)
926*800a58d9SAndroid Build Coastguard Worker        zone = self._GetZoneName(gce_instance.get(constants.INS_KEY_ZONE))
927*800a58d9SAndroid Build Coastguard Worker
928*800a58d9SAndroid Build Coastguard Worker        instance_ip = GetInstanceIP(gce_instance)
929*800a58d9SAndroid Build Coastguard Worker        ip = instance_ip.external or instance_ip.internal
930*800a58d9SAndroid Build Coastguard Worker        project = self._GetProjectName(gce_instance.get(constants.INS_KEY_ZONE))
931*800a58d9SAndroid Build Coastguard Worker        hostname = GetGCEHostName(project, name, zone)
932*800a58d9SAndroid Build Coastguard Worker
933*800a58d9SAndroid Build Coastguard Worker        # Get metadata, webrtc_port will be removed if "cvd fleet" show it.
934*800a58d9SAndroid Build Coastguard Worker        display = None
935*800a58d9SAndroid Build Coastguard Worker        avd_type = None
936*800a58d9SAndroid Build Coastguard Worker        avd_flavor = None
937*800a58d9SAndroid Build Coastguard Worker        webrtc_port = None
938*800a58d9SAndroid Build Coastguard Worker        webrtc_device_id = None
939*800a58d9SAndroid Build Coastguard Worker        for metadata in gce_instance.get("metadata", {}).get("items", []):
940*800a58d9SAndroid Build Coastguard Worker            key = metadata["key"]
941*800a58d9SAndroid Build Coastguard Worker            value = metadata["value"]
942*800a58d9SAndroid Build Coastguard Worker            if key == constants.INS_KEY_DISPLAY:
943*800a58d9SAndroid Build Coastguard Worker                display = value
944*800a58d9SAndroid Build Coastguard Worker            elif key == constants.INS_KEY_AVD_TYPE:
945*800a58d9SAndroid Build Coastguard Worker                avd_type = value
946*800a58d9SAndroid Build Coastguard Worker            elif key == constants.INS_KEY_AVD_FLAVOR:
947*800a58d9SAndroid Build Coastguard Worker                avd_flavor = value
948*800a58d9SAndroid Build Coastguard Worker            elif key == constants.INS_KEY_WEBRTC_PORT:
949*800a58d9SAndroid Build Coastguard Worker                webrtc_port = value
950*800a58d9SAndroid Build Coastguard Worker            elif key == constants.INS_KEY_WEBRTC_DEVICE_ID:
951*800a58d9SAndroid Build Coastguard Worker                webrtc_device_id = value
952*800a58d9SAndroid Build Coastguard Worker        # TODO(176884236): Insert avd information into metadata of instance.
953*800a58d9SAndroid Build Coastguard Worker        if not avd_type and name.startswith(_ACLOUDWEB_INSTANCE_START_STRING):
954*800a58d9SAndroid Build Coastguard Worker            avd_type = constants.TYPE_CF
955*800a58d9SAndroid Build Coastguard Worker
956*800a58d9SAndroid Build Coastguard Worker        # Find ssl tunnel info.
957*800a58d9SAndroid Build Coastguard Worker        adb_port = None
958*800a58d9SAndroid Build Coastguard Worker        vnc_port = None
959*800a58d9SAndroid Build Coastguard Worker        webrtc_forward_port = None
960*800a58d9SAndroid Build Coastguard Worker        device_information = None
961*800a58d9SAndroid Build Coastguard Worker        if ip:
962*800a58d9SAndroid Build Coastguard Worker            forwarded_ports = self.GetAdbVncPortFromSSHTunnel(ip, hostname, avd_type)
963*800a58d9SAndroid Build Coastguard Worker            adb_port = forwarded_ports.adb_port
964*800a58d9SAndroid Build Coastguard Worker            vnc_port = forwarded_ports.vnc_port
965*800a58d9SAndroid Build Coastguard Worker            ssh_tunnel_is_connected = adb_port is not None
966*800a58d9SAndroid Build Coastguard Worker            webrtc_forward_port = utils.GetWebrtcPortFromSSHTunnel(ip)
967*800a58d9SAndroid Build Coastguard Worker
968*800a58d9SAndroid Build Coastguard Worker            adb_device = AdbTools(adb_port)
969*800a58d9SAndroid Build Coastguard Worker            if adb_device.IsAdbConnected():
970*800a58d9SAndroid Build Coastguard Worker                device_information = adb_device.device_information
971*800a58d9SAndroid Build Coastguard Worker                fullname = _GetDeviceFullName("127.0.0.1:%d" % adb_port, name,
972*800a58d9SAndroid Build Coastguard Worker                                              elapsed_time, webrtc_device_id)
973*800a58d9SAndroid Build Coastguard Worker            else:
974*800a58d9SAndroid Build Coastguard Worker                fullname = _GetDeviceFullName("not connected", name,
975*800a58d9SAndroid Build Coastguard Worker                                              elapsed_time, webrtc_device_id)
976*800a58d9SAndroid Build Coastguard Worker        # If instance is terminated, its ip is None.
977*800a58d9SAndroid Build Coastguard Worker        else:
978*800a58d9SAndroid Build Coastguard Worker            ssh_tunnel_is_connected = False
979*800a58d9SAndroid Build Coastguard Worker            fullname = _GetDeviceFullName("terminated", name, elapsed_time,
980*800a58d9SAndroid Build Coastguard Worker                                          webrtc_device_id)
981*800a58d9SAndroid Build Coastguard Worker
982*800a58d9SAndroid Build Coastguard Worker        super().__init__(
983*800a58d9SAndroid Build Coastguard Worker            name=name, fullname=fullname, display=display, ip=ip, status=status,
984*800a58d9SAndroid Build Coastguard Worker            adb_port=adb_port, vnc_port=vnc_port,
985*800a58d9SAndroid Build Coastguard Worker            ssh_tunnel_is_connected=ssh_tunnel_is_connected,
986*800a58d9SAndroid Build Coastguard Worker            createtime=create_time, elapsed_time=elapsed_time, avd_type=avd_type,
987*800a58d9SAndroid Build Coastguard Worker            avd_flavor=avd_flavor, is_local=False,
988*800a58d9SAndroid Build Coastguard Worker            device_information=device_information,
989*800a58d9SAndroid Build Coastguard Worker            zone=zone, webrtc_port=webrtc_port,
990*800a58d9SAndroid Build Coastguard Worker            webrtc_forward_port=webrtc_forward_port)
991*800a58d9SAndroid Build Coastguard Worker
992*800a58d9SAndroid Build Coastguard Worker    @staticmethod
993*800a58d9SAndroid Build Coastguard Worker    def _GetZoneName(zone_info):
994*800a58d9SAndroid Build Coastguard Worker        """Get the zone name from the zone information of gce instance.
995*800a58d9SAndroid Build Coastguard Worker
996*800a58d9SAndroid Build Coastguard Worker        Zone information is like:
997*800a58d9SAndroid Build Coastguard Worker        "https://www.googleapis.com/compute/v1/projects/project/zones/us-central1-c"
998*800a58d9SAndroid Build Coastguard Worker        We want to get "us-central1-c" as zone name.
999*800a58d9SAndroid Build Coastguard Worker
1000*800a58d9SAndroid Build Coastguard Worker        Args:
1001*800a58d9SAndroid Build Coastguard Worker            zone_info: String, zone information of gce instance.
1002*800a58d9SAndroid Build Coastguard Worker
1003*800a58d9SAndroid Build Coastguard Worker        Returns:
1004*800a58d9SAndroid Build Coastguard Worker            Zone name of gce instance. None if zone name can't find.
1005*800a58d9SAndroid Build Coastguard Worker        """
1006*800a58d9SAndroid Build Coastguard Worker        zone_match = _RE_ZONE.match(zone_info)
1007*800a58d9SAndroid Build Coastguard Worker        if zone_match:
1008*800a58d9SAndroid Build Coastguard Worker            return zone_match.group("zone")
1009*800a58d9SAndroid Build Coastguard Worker
1010*800a58d9SAndroid Build Coastguard Worker        logger.debug("Can't get zone name from %s.", zone_info)
1011*800a58d9SAndroid Build Coastguard Worker        return None
1012*800a58d9SAndroid Build Coastguard Worker
1013*800a58d9SAndroid Build Coastguard Worker    @staticmethod
1014*800a58d9SAndroid Build Coastguard Worker    def _GetProjectName(zone_info):
1015*800a58d9SAndroid Build Coastguard Worker        """Get the project name from the zone information of gce instance.
1016*800a58d9SAndroid Build Coastguard Worker
1017*800a58d9SAndroid Build Coastguard Worker        Zone information is like:
1018*800a58d9SAndroid Build Coastguard Worker        "https://www.googleapis.com/compute/v1/projects/project/zones/us-central1-c"
1019*800a58d9SAndroid Build Coastguard Worker        We want to get "project" as project name.
1020*800a58d9SAndroid Build Coastguard Worker
1021*800a58d9SAndroid Build Coastguard Worker        Args:
1022*800a58d9SAndroid Build Coastguard Worker            zone_info: String, zone information of gce instance.
1023*800a58d9SAndroid Build Coastguard Worker
1024*800a58d9SAndroid Build Coastguard Worker        Returns:
1025*800a58d9SAndroid Build Coastguard Worker            Project name of gce instance. None if project name can't find.
1026*800a58d9SAndroid Build Coastguard Worker        """
1027*800a58d9SAndroid Build Coastguard Worker        project_match = _RE_PROJECT.match(zone_info)
1028*800a58d9SAndroid Build Coastguard Worker        if project_match:
1029*800a58d9SAndroid Build Coastguard Worker            return project_match.group("project")
1030*800a58d9SAndroid Build Coastguard Worker
1031*800a58d9SAndroid Build Coastguard Worker        logger.debug("Can't get project name from %s.", zone_info)
1032*800a58d9SAndroid Build Coastguard Worker        return None
1033*800a58d9SAndroid Build Coastguard Worker
1034*800a58d9SAndroid Build Coastguard Worker    @staticmethod
1035*800a58d9SAndroid Build Coastguard Worker    def GetAdbVncPortFromSSHTunnel(ip, hostname, avd_type):
1036*800a58d9SAndroid Build Coastguard Worker        """Get forwarding adb and vnc port from ssh tunnel.
1037*800a58d9SAndroid Build Coastguard Worker
1038*800a58d9SAndroid Build Coastguard Worker        Args:
1039*800a58d9SAndroid Build Coastguard Worker            ip: String, ip address.
1040*800a58d9SAndroid Build Coastguard Worker            hostname: String, hostname of GCE instance.
1041*800a58d9SAndroid Build Coastguard Worker            avd_type: String, the AVD type.
1042*800a58d9SAndroid Build Coastguard Worker
1043*800a58d9SAndroid Build Coastguard Worker        Returns:
1044*800a58d9SAndroid Build Coastguard Worker            NamedTuple ForwardedPorts(vnc_port, adb_port) holding the ports
1045*800a58d9SAndroid Build Coastguard Worker            used in the ssh forwarded call. Both fields are integers.
1046*800a58d9SAndroid Build Coastguard Worker        """
1047*800a58d9SAndroid Build Coastguard Worker        if avd_type not in utils.AVD_PORT_DICT:
1048*800a58d9SAndroid Build Coastguard Worker            return utils.ForwardedPorts(vnc_port=None, adb_port=None)
1049*800a58d9SAndroid Build Coastguard Worker
1050*800a58d9SAndroid Build Coastguard Worker        default_vnc_port = utils.AVD_PORT_DICT[avd_type].vnc_port
1051*800a58d9SAndroid Build Coastguard Worker        default_adb_port = utils.AVD_PORT_DICT[avd_type].adb_port
1052*800a58d9SAndroid Build Coastguard Worker        # TODO(165888525): Align the SSH tunnel for the order of adb port and
1053*800a58d9SAndroid Build Coastguard Worker        # vnc port.
1054*800a58d9SAndroid Build Coastguard Worker        re_pattern = re.compile(_RE_SSH_TUNNEL_PATTERN %
1055*800a58d9SAndroid Build Coastguard Worker                                (_RE_GROUP_ADB, default_adb_port,
1056*800a58d9SAndroid Build Coastguard Worker                                 _RE_GROUP_VNC, default_vnc_port, ip, hostname))
1057*800a58d9SAndroid Build Coastguard Worker        adb_port = None
1058*800a58d9SAndroid Build Coastguard Worker        vnc_port = None
1059*800a58d9SAndroid Build Coastguard Worker        process_output = utils.CheckOutput(constants.COMMAND_PS)
1060*800a58d9SAndroid Build Coastguard Worker        for line in process_output.splitlines():
1061*800a58d9SAndroid Build Coastguard Worker            match = re_pattern.match(line)
1062*800a58d9SAndroid Build Coastguard Worker            if match:
1063*800a58d9SAndroid Build Coastguard Worker                adb_port = int(match.group(_RE_GROUP_ADB))
1064*800a58d9SAndroid Build Coastguard Worker                vnc_port = int(match.group(_RE_GROUP_VNC))
1065*800a58d9SAndroid Build Coastguard Worker                break
1066*800a58d9SAndroid Build Coastguard Worker
1067*800a58d9SAndroid Build Coastguard Worker        logger.debug(("gathering detail for ssh tunnel. "
1068*800a58d9SAndroid Build Coastguard Worker                      "IP:%s, forwarding (adb:%s, vnc:%s)"), ip, adb_port,
1069*800a58d9SAndroid Build Coastguard Worker                     vnc_port)
1070*800a58d9SAndroid Build Coastguard Worker
1071*800a58d9SAndroid Build Coastguard Worker        return utils.ForwardedPorts(vnc_port=vnc_port, adb_port=adb_port)
1072