1# Copyright 2020 - The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""cvd_runtime_config class.""" 15 16import json 17import os 18import re 19 20from acloud import errors 21 22_CFG_KEY_CROSVM_BINARY = "crosvm_binary" 23_CFG_KEY_X_RES = "x_res" 24_CFG_KEY_Y_RES = "y_res" 25_CFG_KEY_DPI = "dpi" 26_CFG_KEY_VIRTUAL_DISK_PATHS = "virtual_disk_paths" 27_CFG_KEY_INSTANCES = "instances" 28_CFG_KEY_ADB_IP_PORT = "adb_ip_and_port" 29_CFG_KEY_INSTANCE_DIR = "instance_dir" 30_CFG_KEY_VNC_PORT = "vnc_server_port" 31# The adb port field name changes from "host_port" to "adb_host_port". 32_CFG_KEY_ADB_PORT = "host_port" 33_CFG_KEY_ADB_HOST_PORT = "adb_host_port" 34_CFG_KEY_ENABLE_WEBRTC = "enable_webrtc" 35# TODO(148648620): Check instance_home_[id] for backward compatible. 36_RE_LOCAL_INSTANCE_ID = re.compile(r".+(?:local-instance-|instance_home_)" 37 r"(?P<ins_id>\d+).+") 38 39 40def _GetIdFromInstanceDirStr(instance_dir): 41 """Look for instance id from the path of instance dir. 42 43 Args: 44 instance_dir: String, path of instance_dir. 45 46 Returns: 47 String of instance id. 48 """ 49 match = _RE_LOCAL_INSTANCE_ID.match(instance_dir) 50 if match: 51 return match.group("ins_id") 52 53 # To support the device which is not created by acloud. 54 if os.path.expanduser("~") in instance_dir: 55 return "1" 56 57 return None 58 59 60class CvdRuntimeConfig(): 61 """The class that hold the information from cuttlefish_config.json. 62 63 The example of cuttlefish_config.json 64 { 65 "memory_mb" : 4096, 66 "cpus" : 2, 67 "dpi" : 320, 68 "virtual_disk_paths" : 69 [ 70 "/path-to-image" 71 ], 72 "adb_ip_and_port" : "0.0.0.0:6520", 73 "instance_dir" : "/path-to-instance-dir", 74 } 75 76 If we launched multiple local instances, the config will be as below: 77 { 78 "memory_mb" : 4096, 79 "cpus" : 2, 80 "dpi" : 320, 81 "instances" : 82 { 83 "1" : 84 { 85 "adb_ip_and_port" : "0.0.0.0:6520", 86 "instance_dir" : "/path-to-instance-dir", 87 "virtual_disk_paths" : 88 [ 89 "/path-to-image" 90 ], 91 } 92 } 93 } 94 95 If the avd enable the webrtc, the config will be as below: 96 { 97 "enable_webrtc" : true, 98 "vnc_server_binary" : "/home/vsoc-01/bin/vnc_server", 99 "webrtc_assets_dir" : "/home/vsoc-01/usr/share/webrtc/assets", 100 "webrtc_binary" : "/home/vsoc-01/bin/webRTC", 101 "webrtc_certs_dir" : "/home/vsoc-01/usr/share/webrtc/certs", 102 "webrtc_enable_adb_websocket" : false, 103 "webrtc_public_ip" : "0.0.0.0", 104 } 105 106 """ 107 108 def __init__(self, config_path=None, raw_data=None): 109 if not config_path and not raw_data: 110 raise errors.ConfigError("No cuttlefish config found!") 111 self._config_path = config_path 112 self._instance_id = "1" if raw_data else _GetIdFromInstanceDirStr( 113 config_path) 114 self._config_dict = self._GetCuttlefishRuntimeConfig(config_path, 115 raw_data) 116 self._x_res = self._config_dict.get(_CFG_KEY_X_RES) 117 self._y_res = self._config_dict.get(_CFG_KEY_Y_RES) 118 self._dpi = self._config_dict.get(_CFG_KEY_DPI) 119 crosvm_bin = self._config_dict.get(_CFG_KEY_CROSVM_BINARY) 120 self._cvd_tools_path = (os.path.dirname(crosvm_bin) 121 if crosvm_bin else None) 122 123 # Below properties will be collected inside of instance id node if there 124 # are more than one instance. 125 self._instance_dir = self._config_dict.get(_CFG_KEY_INSTANCE_DIR) 126 self._vnc_port = self._config_dict.get(_CFG_KEY_VNC_PORT) 127 self._adb_port = (self._config_dict.get(_CFG_KEY_ADB_PORT) or 128 self._config_dict.get(_CFG_KEY_ADB_HOST_PORT)) 129 self._adb_ip_port = self._config_dict.get(_CFG_KEY_ADB_IP_PORT) 130 self._virtual_disk_paths = self._config_dict.get( 131 _CFG_KEY_VIRTUAL_DISK_PATHS) 132 self._enable_webrtc = self._config_dict.get(_CFG_KEY_ENABLE_WEBRTC) 133 if not self._instance_dir: 134 ins_cfg = self._config_dict.get(_CFG_KEY_INSTANCES) 135 ins_dict = ins_cfg.get(self._instance_id) 136 if not ins_dict: 137 raise errors.ConfigError("instances[%s] property does not exist" 138 " in: %s" % 139 (self._instance_id, config_path)) 140 self._instance_dir = ins_dict.get(_CFG_KEY_INSTANCE_DIR) 141 self._vnc_port = ins_dict.get(_CFG_KEY_VNC_PORT) 142 self._adb_port = (ins_dict.get(_CFG_KEY_ADB_PORT) or 143 ins_dict.get(_CFG_KEY_ADB_HOST_PORT)) 144 self._adb_ip_port = ins_dict.get(_CFG_KEY_ADB_IP_PORT) 145 self._virtual_disk_paths = ins_dict.get(_CFG_KEY_VIRTUAL_DISK_PATHS) 146 147 @staticmethod 148 def _GetCuttlefishRuntimeConfig(runtime_cf_config_path, raw_data=None): 149 """Get and parse cuttlefish_config.json. 150 151 Args: 152 runtime_cf_config_path: String, path of the cvd runtime config. 153 raw_data: String, data of the cvd runtime config. 154 155 Returns: 156 A dictionary that parsed from cuttlefish runtime config. 157 158 Raises: 159 errors.ConfigError: if file not found or config load failed. 160 """ 161 if raw_data: 162 # if remote instance couldn't fetch the config will return message such as 163 # 'cat: .../cuttlefish_config.json: No such file or directory'. 164 # Add this condition to prevent from JSONDecodeError. 165 try: 166 return json.loads(raw_data) 167 except ValueError as e: 168 raise errors.ConfigError( 169 "An exception happened when loading the raw_data of the " 170 "cvd runtime config:\n%s" % str(e)) 171 if not os.path.exists(runtime_cf_config_path): 172 raise errors.ConfigError( 173 "file does not exist: %s" % runtime_cf_config_path) 174 with open(runtime_cf_config_path, "r") as cf_config: 175 return json.load(cf_config) 176 177 @property 178 def cvd_tools_path(self): 179 """Return string of the path to the cvd tools.""" 180 return self._cvd_tools_path 181 182 @property 183 def x_res(self): 184 """Return x_res.""" 185 return self._x_res 186 187 @property 188 def y_res(self): 189 """Return y_res.""" 190 return self._y_res 191 192 @property 193 def dpi(self): 194 """Return dpi.""" 195 return self._dpi 196 197 @property 198 def adb_ip_port(self): 199 """Return adb_ip_port.""" 200 return self._adb_ip_port 201 202 @property 203 def instance_dir(self): 204 """Return instance_dir.""" 205 return self._instance_dir 206 207 @property 208 def vnc_port(self): 209 """Return vnc_port.""" 210 return self._vnc_port 211 212 @property 213 def adb_port(self): 214 """Return adb_port.""" 215 return self._adb_port 216 217 @property 218 def config_path(self): 219 """Return config_path.""" 220 return self._config_path 221 222 @property 223 def virtual_disk_paths(self): 224 """Return virtual_disk_paths""" 225 return self._virtual_disk_paths 226 227 @property 228 def instance_id(self): 229 """Return _instance_id""" 230 return self._instance_id 231 232 @property 233 def enable_webrtc(self): 234 """Return _enable_webrtc""" 235 return self._enable_webrtc 236