• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright 2018 - The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16"""Common code used by acloud create methods/classes."""
17
18import logging
19import os
20import re
21import shutil
22
23from acloud import errors
24from acloud.internal import constants
25from acloud.internal.lib import android_build_client
26from acloud.internal.lib import auth
27from acloud.internal.lib import utils
28
29
30logger = logging.getLogger(__name__)
31
32
33def ParseKeyValuePairArgs(dict_str, item_separator=",", key_value_separator=":"):
34    """Helper function to initialize a dict object from string.
35
36    e.g.
37    cpu:2,dpi:240,resolution:1280x800
38    -> {"cpu":"2", "dpi":"240", "resolution":"1280x800"}
39
40    Args:
41        dict_str: A String to be converted to dict object.
42        item_separator: String character to separate items.
43        key_value_separator: String character to separate key and value.
44
45    Returns:
46        Dict created from key:val pairs in dict_str.
47
48    Raises:
49        error.MalformedDictStringError: If dict_str is malformed.
50    """
51    args_dict = {}
52    if not dict_str:
53        return args_dict
54
55    for item in dict_str.split(item_separator):
56        if key_value_separator not in item:
57            raise errors.MalformedDictStringError(
58                "Expecting ':' in '%s' to make a key-val pair" % item)
59        key, value = item.split(key_value_separator)
60        if not value or not key:
61            raise errors.MalformedDictStringError(
62                "Missing key or value in %s, expecting form of 'a:b'" % item)
63        args_dict[key.strip()] = value.strip()
64
65    return args_dict
66
67
68def GetCvdHostPackage():
69    """Get cvd host package path.
70
71    Look for the host package in $ANDROID_HOST_OUT and dist dir then verify
72    existence and get cvd host package path.
73
74    Return:
75        A string, the path to the host package.
76
77    Raises:
78        errors.GetCvdLocalHostPackageError: Can't find cvd host package.
79    """
80    dirs_to_check = list(
81        filter(None, [
82            os.environ.get(constants.ENV_ANDROID_SOONG_HOST_OUT),
83            os.environ.get(constants.ENV_ANDROID_HOST_OUT)
84        ]))
85    dist_dir = utils.GetDistDir()
86    if dist_dir:
87        dirs_to_check.append(dist_dir)
88
89    for path in dirs_to_check:
90        cvd_host_package = os.path.join(path, constants.CVD_HOST_PACKAGE)
91        if os.path.exists(cvd_host_package):
92            logger.debug("cvd host package: %s", cvd_host_package)
93            return cvd_host_package
94    raise errors.GetCvdLocalHostPackageError(
95        "Can't find the cvd host package (Try lunching a cuttlefish target"
96        " like aosp_cf_x86_phone-userdebug and running 'm'): \n%s" %
97        '\n'.join(dirs_to_check))
98
99
100def FindLocalImage(path, default_name_pattern):
101    """Find an image file in the given path.
102
103    Args:
104        path: The path to the file or the parent directory.
105        default_name_pattern: A regex string, the file to look for if the path
106                              is a directory.
107
108    Returns:
109        The absolute path to the image file.
110
111    Raises:
112        errors.GetLocalImageError if this method cannot find exactly one image.
113    """
114    path = os.path.abspath(path)
115    if os.path.isdir(path):
116        names = [name for name in os.listdir(path) if
117                 re.fullmatch(default_name_pattern, name)]
118        if not names:
119            raise errors.GetLocalImageError("No image in %s." % path)
120        if len(names) != 1:
121            raise errors.GetLocalImageError("More than one image in %s: %s" %
122                                            (path, " ".join(names)))
123        path = os.path.join(path, names[0])
124    if os.path.isfile(path):
125        return path
126    raise errors.GetLocalImageError("%s is not a file." % path)
127
128
129def DownloadRemoteArtifact(cfg, build_target, build_id, artifact, extract_path,
130                           decompress=False):
131    """Download remote artifact.
132
133    Args:
134        cfg: An AcloudConfig instance.
135        build_target: String, the build target, e.g. cf_x86_phone-userdebug.
136        build_id: String, Build id, e.g. "2263051", "P2804227"
137        artifact: String, zip image or cvd host package artifact.
138        extract_path: String, a path include extracted files.
139        decompress: Boolean, if true decompress the artifact.
140    """
141    build_client = android_build_client.AndroidBuildClient(
142        auth.CreateCredentials(cfg))
143    temp_file = os.path.join(extract_path, artifact)
144    build_client.DownloadArtifact(
145        build_target,
146        build_id,
147        artifact,
148        temp_file)
149    if decompress:
150        utils.Decompress(temp_file, extract_path)
151        try:
152            os.remove(temp_file)
153            logger.debug("Deleted temporary file %s", temp_file)
154        except OSError as e:
155            logger.error("Failed to delete temporary file: %s", str(e))
156
157
158def PrepareLocalInstanceDir(instance_dir, avd_spec):
159    """Create a directory for a local cuttlefish or goldfish instance.
160
161    If avd_spec has the local instance directory, this method creates a
162    symbolic link from instance_dir to the directory. Otherwise, it creates an
163    empty directory at instance_dir.
164
165    Args:
166        instance_dir: The absolute path to the default instance directory.
167        avd_spec: AVDSpec object that provides the instance directory.
168    """
169    if os.path.islink(instance_dir):
170        os.remove(instance_dir)
171    else:
172        shutil.rmtree(instance_dir, ignore_errors=True)
173
174    if avd_spec.local_instance_dir:
175        abs_instance_dir = os.path.abspath(avd_spec.local_instance_dir)
176        if instance_dir != abs_instance_dir:
177            os.symlink(abs_instance_dir, instance_dir)
178            return
179    if not os.path.exists(instance_dir):
180        os.makedirs(instance_dir)
181