Merge "Add new error type for GCE quota issue error."
diff --git a/Android.bp b/Android.bp
index cb49b12..c980e44 100644
--- a/Android.bp
+++ b/Android.bp
@@ -42,7 +42,7 @@
],
data: [
"public/data/default.config",
- ":acloud_version",
+ ":acloud_version",
],
libs: [
"acloud_create",
diff --git a/create/avd_spec.py b/create/avd_spec.py
index b6a42ea..905017f 100644
--- a/create/avd_spec.py
+++ b/create/avd_spec.py
@@ -552,7 +552,7 @@
self._remote_image[constants.BUILD_BRANCH])
self._remote_image[constants.CHEEPS_BETTY_IMAGE] = (
- args.cheeps_betty_image)
+ args.cheeps_betty_image or self._cfg.betty_image)
# Process system image and kernel image.
self._system_build_info = {constants.BUILD_ID: args.system_build_id,
diff --git a/create/avd_spec_test.py b/create/avd_spec_test.py
index 85c0b89..82de209 100644
--- a/create/avd_spec_test.py
+++ b/create/avd_spec_test.py
@@ -27,6 +27,7 @@
from acloud.internal.lib import driver_test_lib
from acloud.internal.lib import utils
from acloud.list import list as list_instances
+from acloud.public import config
# pylint: disable=invalid-name,protected-access
@@ -325,16 +326,23 @@
self.AvdSpec._ProcessRemoteBuildArgs(self.args)
self.assertTrue(self.AvdSpec.avd_type == "cuttlefish")
+ # Setup acloud config with betty_image spec
+ cfg = mock.MagicMock()
+ cfg.betty_image = 'foobarbaz'
+ self.Patch(config, 'GetAcloudConfig', return_value=cfg)
+ self.AvdSpec = avd_spec.AVDSpec(self.args)
+ # --betty-image from cmdline should override config
self.args.cheeps_betty_image = 'abcdefg'
self.AvdSpec._ProcessRemoteBuildArgs(self.args)
self.assertEqual(
self.AvdSpec.remote_image[constants.CHEEPS_BETTY_IMAGE],
self.args.cheeps_betty_image)
+ # acloud config value is used otherwise
self.args.cheeps_betty_image = None
self.AvdSpec._ProcessRemoteBuildArgs(self.args)
self.assertEqual(
self.AvdSpec.remote_image[constants.CHEEPS_BETTY_IMAGE],
- self.args.cheeps_betty_image)
+ cfg.betty_image)
def testEscapeAnsi(self):
diff --git a/create/goldfish_local_image_local_instance.py b/create/goldfish_local_image_local_instance.py
index c066b3c..4141bd8 100644
--- a/create/goldfish_local_image_local_instance.py
+++ b/create/goldfish_local_image_local_instance.py
@@ -238,13 +238,7 @@
emulator_path = self._FindEmulatorBinary(avd_spec.local_tool_dirs)
emulator_path = os.path.abspath(emulator_path)
- image_dir = os.path.abspath(avd_spec.local_image_dir)
-
- if not (os.path.isfile(os.path.join(image_dir, _SYSTEM_IMAGE_NAME)) or
- os.path.isfile(os.path.join(image_dir,
- _SYSTEM_QEMU_IMAGE_NAME))):
- raise errors.GetLocalImageError("No system image in %s." %
- image_dir)
+ image_dir = self._FindImageDir(avd_spec.local_image_dir)
# TODO(b/141898893): In Android build environment, emulator gets build
# information from $ANDROID_PRODUCT_OUT/system/build.prop.
@@ -343,6 +337,35 @@
raise errors.GetSdkRepoPackageError(_MISSING_EMULATOR_MSG)
@staticmethod
+ def _FindImageDir(image_dir):
+ """Find emulator images in the directory.
+
+ In build environment, the images are in $ANDROID_PRODUCT_OUT.
+ In an extracted SDK repository, the images are in the subdirectory
+ named after the CPU architecture.
+
+ Args:
+ image_dir: The path given by the environment variable or the user.
+
+ Returns:
+ The directory containing the emulator images.
+
+ Raises:
+ errors.GetLocalImageError if the images are not found.
+ """
+ entries = os.listdir(image_dir)
+ if len(entries) == 1:
+ first_entry = os.path.join(image_dir, entries[0])
+ if os.path.isdir(first_entry):
+ image_dir = first_entry
+
+ if (os.path.isfile(os.path.join(image_dir, _SYSTEM_QEMU_IMAGE_NAME)) or
+ os.path.isfile(os.path.join(image_dir, _SYSTEM_IMAGE_NAME))):
+ return image_dir
+
+ raise errors.GetLocalImageError("No system image in %s." % image_dir)
+
+ @staticmethod
def _IsEmulatorRunning(adb):
"""Check existence of an emulator by sending an empty command.
diff --git a/create/goldfish_local_image_local_instance_test.py b/create/goldfish_local_image_local_instance_test.py
index 54b5944..98690a1 100644
--- a/create/goldfish_local_image_local_instance_test.py
+++ b/create/goldfish_local_image_local_instance_test.py
@@ -180,8 +180,10 @@
"""Test _CreateAVD with SDK repository files."""
self._SetUpMocks(mock_popen, mock_utils, mock_instance)
- self._CreateEmptyFile(os.path.join(self._image_dir, "system.img"))
- self._CreateEmptyFile(os.path.join(self._image_dir, "build.prop"))
+ self._CreateEmptyFile(os.path.join(self._image_dir, "x86",
+ "system.img"))
+ self._CreateEmptyFile(os.path.join(self._image_dir, "x86",
+ "build.prop"))
mock_avd_spec = mock.Mock(flavor="phone",
boot_timeout_secs=None,
@@ -214,7 +216,7 @@
self._mock_proc.poll.assert_called()
self.assertTrue(os.path.isfile(
- os.path.join(self._image_dir, "system", "build.prop")))
+ os.path.join(self._image_dir, "x86", "system", "build.prop")))
# pylint: disable=protected-access
@mock.patch("acloud.create.goldfish_local_image_local_instance.instance."
diff --git a/internal/proto/user_config.proto b/internal/proto/user_config.proto
index 8a1f53e..c83f15c 100755
--- a/internal/proto/user_config.proto
+++ b/internal/proto/user_config.proto
@@ -109,4 +109,7 @@
// [CVD only] Enable multi stage function.
optional bool enable_multi_stage = 30;
+
+ // [CHEEPS only] The name of the L1 betty image (used with Cheeps controller)
+ optional string betty_image = 31;
}
diff --git a/list/list.py b/list/list.py
index 42487f9..4598de2 100644
--- a/list/list.py
+++ b/list/list.py
@@ -275,12 +275,13 @@
return instances_list[0]
-def _FilterInstancesByNames(instances, names):
+def _FilterInstancesByNames(instances, names, all_match=True):
"""Find instances by names.
Args:
instances: Collection of Instance objects.
names: Collection of strings, the names of the instances to search for.
+ all_match: Boolean, True to raise error if any instance is missing.
Returns:
List of Instance objects.
@@ -297,17 +298,18 @@
else:
missing_instance_names.append(name)
- if missing_instance_names:
+ if missing_instance_names and all_match:
raise errors.NoInstancesFound("Did not find the following instances: %s" %
" ".join(missing_instance_names))
return found_instances
-def GetLocalInstancesByNames(names):
+def GetLocalInstancesByNames(names, all_match=True):
"""Get local cuttlefish and goldfish instances by names.
Args:
names: Collection of instance names.
+ all_match: Boolean, True to raise error if any instance is missing.
Returns:
List consisting of LocalInstance and LocalGoldfishInstance objects.
@@ -331,7 +333,7 @@
return _FilterInstancesByNames(
_GetLocalCuttlefishInstances(id_cfg_pairs) +
instance.LocalGoldfishInstance.GetExistingInstances(),
- names)
+ names, all_match)
def GetInstancesFromInstanceNames(cfg, instance_names):
@@ -350,8 +352,8 @@
errors.NoInstancesFound: No instances found.
"""
return _FilterInstancesByNames(
- GetLocalInstancesByNames(instance_names) + GetRemoteInstances(cfg),
- instance_names)
+ GetLocalInstancesByNames(instance_names, all_match=False) +
+ GetRemoteInstances(cfg), instance_names)
def FilterInstancesByAdbPort(instances, adb_port):
diff --git a/list/list_test.py b/list/list_test.py
index c6d04bd..3fd9857 100644
--- a/list/list_test.py
+++ b/list/list_test.py
@@ -115,6 +115,11 @@
ins_list = list_instance.GetLocalInstancesByNames(
["local-instance-1", "local-instance-6"])
+ # test get instance without raising error
+ ins_list = list_instance.GetLocalInstancesByNames(
+ ["local-instance-1", "local-instance-6"], all_match=False)
+ self.assertEqual(1, len(ins_list))
+
# pylint: disable=attribute-defined-outside-init
def testFilterInstancesByAdbPort(self):
"""test FilterInstancesByAdbPort."""
diff --git a/public/config.py b/public/config.py
index dcb856c..9fe5f83 100755
--- a/public/config.py
+++ b/public/config.py
@@ -224,6 +224,7 @@
self.stable_cheeps_host_image_project = (
usr_cfg.stable_cheeps_host_image_project or
internal_cfg.default_usr_cfg.stable_cheeps_host_image_project)
+ self.betty_image = usr_cfg.betty_image
self.extra_args_ssh_tunnel = usr_cfg.extra_args_ssh_tunnel
diff --git a/public/config_test.py b/public/config_test.py
index 88f2899..cd0cf95 100644
--- a/public/config_test.py
+++ b/public/config_test.py
@@ -54,6 +54,7 @@
hw_property: "cpu:3,resolution:1080x1920,dpi:480,memory:4g,disk:10g"
extra_scopes: "scope1"
extra_scopes: "scope2"
+betty_image: "fake_betty_image"
"""
INTERNAL_CONFIG = """
@@ -143,6 +144,7 @@
"cpu:3,resolution:1080x1920,dpi:480,memory:4g,"
"disk:10g")
self.assertEqual(cfg.extra_scopes, ["scope1", "scope2"])
+ self.assertEqual(cfg.betty_image, "fake_betty_image")
# pylint: disable=protected-access
@mock.patch("os.makedirs")