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_DISPLAY_CONFIGS = "display_configs" 24_CFG_KEY_VIRTUAL_DISK_PATHS = "virtual_disk_paths" 25_CFG_KEY_INSTANCES = "instances" 26_CFG_KEY_ADB_IP_PORT = "adb_ip_and_port" 27_CFG_KEY_INSTANCE_DIR = "instance_dir" 28_CFG_KEY_ROOT_DIR = "root_dir" 29_CFG_KEY_VNC_PORT = "vnc_server_port" 30# The adb port field name changes from "host_port" to "adb_host_port". 31_CFG_KEY_ADB_PORT = "host_port" 32_CFG_KEY_ADB_HOST_PORT = "adb_host_port" 33_CFG_KEY_ENABLE_WEBRTC = "enable_webrtc" 34# TODO(148648620): Check instance_home_[id] for backward compatible. 35_RE_LOCAL_INSTANCE_ID = re.compile(r".+(?:local-instance-|instance_home_)" 36 r"(?P<ins_id>\d+).+") 37 38 39def _GetIdFromInstanceDirStr(instance_dir, config_dict): 40 """Look for instance id from the path of instance dir. 41 42 Args: 43 instance_dir: String, path of instance_dir. 44 45 Returns: 46 String of instance id. 47 """ 48 match = _RE_LOCAL_INSTANCE_ID.match(instance_dir) 49 if match: 50 return match.group("ins_id") 51 52 # To support the device which is not created by acloud. 53 if os.path.expanduser("~") in instance_dir: 54 if config_dict: 55 instances = config_dict.get(_CFG_KEY_INSTANCES) 56 if instances: 57 return min(instances.keys()) 58 else: 59 # Old runtime config doesn't have "instances" information. 60 return "1" 61 else: 62 return "1" 63 64 return None 65 66 67class CvdRuntimeConfig(): 68 """The class that hold the information from cuttlefish_config.json. 69 70 The example of cuttlefish_config.json 71 { 72 "memory_mb" : 4096, 73 "cpus" : 2, 74 "display_configs" : 75 [ 76 { 77 "dpi" : 160, 78 "x_res" : 1280, 79 "y_res" : 700 80 } 81 ], 82 "dpi" : 320, 83 "virtual_disk_paths" : 84 [ 85 "/path-to-image" 86 ], 87 "adb_ip_and_port" : "0.0.0.0:6520", 88 "instance_dir" : "/path-to-instance-dir", 89 } 90 91 If we launched multiple local instances, the config will be as below: 92 { 93 "memory_mb" : 4096, 94 "cpus" : 2, 95 "dpi" : 320, 96 "instances" : 97 { 98 "1" : 99 { 100 "adb_ip_and_port" : "0.0.0.0:6520", 101 "instance_dir" : "/path-to-instance-dir", 102 "webrtc_device_id" : "cvd-1", 103 "virtual_disk_paths" : 104 [ 105 "/path-to-image" 106 ], 107 } 108 } 109 } 110 111 If the avd enable the webrtc, the config will be as below: 112 { 113 "enable_webrtc" : true, 114 "vnc_server_binary" : "/home/vsoc-01/bin/vnc_server", 115 "webrtc_assets_dir" : "/home/vsoc-01/usr/share/webrtc/assets", 116 "webrtc_binary" : "/home/vsoc-01/bin/webRTC", 117 "webrtc_certs_dir" : "/home/vsoc-01/usr/share/webrtc/certs", 118 "webrtc_public_ip" : "0.0.0.0", 119 } 120 121 """ 122 123 def __init__(self, config_path=None, raw_data=None): 124 if not config_path and not raw_data: 125 raise errors.ConfigError("No cuttlefish config found!") 126 self._config_path = config_path 127 self._config_dict = self._GetCuttlefishRuntimeConfig(config_path, 128 raw_data) 129 self._instance_id = "1" if raw_data else _GetIdFromInstanceDirStr( 130 config_path, self._config_dict) 131 self._instances = self._config_dict.get(_CFG_KEY_INSTANCES) 132 # Old runtime config doesn't have "instances" information. 133 self._instance_ids = list(self._instances.keys()) if self._instances else ["1"] 134 self._display_configs = self._config_dict.get(_CFG_KEY_DISPLAY_CONFIGS, {}) 135 self._root_dir = self._config_dict.get(_CFG_KEY_ROOT_DIR) 136 crosvm_bin = self._config_dict.get(_CFG_KEY_CROSVM_BINARY) 137 self._cvd_tools_path = (os.path.dirname(crosvm_bin) 138 if crosvm_bin else None) 139 140 # Below properties will be collected inside of instance id node if there 141 # are more than one instance. 142 self._instance_dir = self._config_dict.get(_CFG_KEY_INSTANCE_DIR) 143 self._vnc_port = self._config_dict.get(_CFG_KEY_VNC_PORT) 144 self._adb_port = (self._config_dict.get(_CFG_KEY_ADB_PORT) or 145 self._config_dict.get(_CFG_KEY_ADB_HOST_PORT)) 146 self._adb_ip_port = self._config_dict.get(_CFG_KEY_ADB_IP_PORT) 147 self._virtual_disk_paths = self._config_dict.get( 148 _CFG_KEY_VIRTUAL_DISK_PATHS) 149 self._enable_webrtc = self._config_dict.get(_CFG_KEY_ENABLE_WEBRTC) 150 if not self._instance_dir: 151 ins_dict = self._instances.get(self._instance_id) 152 if not ins_dict: 153 raise errors.ConfigError("instances[%s] property does not exist" 154 " in: %s" % 155 (self._instance_id, config_path)) 156 self._instance_dir = ins_dict.get(_CFG_KEY_INSTANCE_DIR) 157 self._vnc_port = ins_dict.get(_CFG_KEY_VNC_PORT) 158 self._adb_port = (ins_dict.get(_CFG_KEY_ADB_PORT) or 159 ins_dict.get(_CFG_KEY_ADB_HOST_PORT)) 160 self._adb_ip_port = ins_dict.get(_CFG_KEY_ADB_IP_PORT) 161 self._virtual_disk_paths = ins_dict.get(_CFG_KEY_VIRTUAL_DISK_PATHS) 162 if not self._cvd_tools_path: 163 self._cvd_tools_path = os.path.dirname( 164 ins_dict.get(_CFG_KEY_CROSVM_BINARY)) 165 166 @staticmethod 167 def _GetCuttlefishRuntimeConfig(runtime_cf_config_path, raw_data=None): 168 """Get and parse cuttlefish_config.json. 169 170 Args: 171 runtime_cf_config_path: String, path of the cvd runtime config. 172 raw_data: String, data of the cvd runtime config. 173 174 Returns: 175 A dictionary that parsed from cuttlefish runtime config. 176 177 Raises: 178 errors.ConfigError: if file not found or config load failed. 179 """ 180 if raw_data: 181 # if remote instance couldn't fetch the config will return message such as 182 # 'cat: .../cuttlefish_config.json: No such file or directory'. 183 # Add this condition to prevent from JSONDecodeError. 184 try: 185 return json.loads(raw_data) 186 except ValueError as e: 187 raise errors.ConfigError( 188 "An exception happened when loading the raw_data of the " 189 "cvd runtime config:\n%s" % str(e)) 190 if not os.path.exists(runtime_cf_config_path): 191 raise errors.ConfigError( 192 "file does not exist: %s" % runtime_cf_config_path) 193 with open(runtime_cf_config_path, "r") as cf_config: 194 return json.load(cf_config) 195 196 @property 197 def cvd_tools_path(self): 198 """Return string of the path to the cvd tools.""" 199 return self._cvd_tools_path 200 201 @property 202 def display_configs(self): 203 """Return display_configs.""" 204 return self._display_configs 205 206 @property 207 def adb_ip_port(self): 208 """Return adb_ip_port.""" 209 return self._adb_ip_port 210 211 @property 212 def instance_dir(self): 213 """Return instance_dir.""" 214 return self._instance_dir 215 216 @property 217 def root_dir(self): 218 """Return root_dir.""" 219 return self._root_dir 220 221 @property 222 def vnc_port(self): 223 """Return vnc_port.""" 224 return self._vnc_port 225 226 @property 227 def adb_port(self): 228 """Return adb_port.""" 229 return self._adb_port 230 231 @property 232 def config_path(self): 233 """Return config_path.""" 234 return self._config_path 235 236 @property 237 def virtual_disk_paths(self): 238 """Return virtual_disk_paths""" 239 return self._virtual_disk_paths 240 241 @property 242 def instance_id(self): 243 """Return _instance_id""" 244 return self._instance_id 245 246 @property 247 def instance_ids(self): 248 """Return _instance_ids""" 249 return self._instance_ids 250 251 @property 252 def instances(self): 253 """Return _instances""" 254 return self._instances 255 256 @property 257 def enable_webrtc(self): 258 """Return _enable_webrtc""" 259 return self._enable_webrtc 260