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