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