acloud: Cherrypick cl/203969178
Bug: None
Test: ./run_tests.sh
Test: m acloud_test && acloud_test
Test: m acloud && acloud create_gf
Change-Id: I28e292f0406b631a20ce9fa95b5a51cd7822f59f
diff --git a/public/acloud_main.py b/public/acloud_main.py
index 196d404..1b0da84 100644
--- a/public/acloud_main.py
+++ b/public/acloud_main.py
@@ -310,14 +310,17 @@
raise errors.CommandArgError(
"Must specify --build_id and --build_target at the same time.")
- if parsed_args.which in [CMD_CREATE_CUTTLEFISH, CMD_CREATE_GOLDFISH]:
+ if parsed_args.which == CMD_CREATE_CUTTLEFISH:
if not parsed_args.build_id or not parsed_args.build_target:
raise errors.CommandArgError(
"Must specify --build_id and --build_target")
if parsed_args.which == CMD_CREATE_GOLDFISH:
- if not parsed_args.emulator_build_id:
- raise errors.CommandArgError("Must specify --emulator_build_id")
+ if not parsed_args.emulator_build_id and not parsed_args.build_id:
+ raise errors.CommandArgError("Must specify either "
+ "--emulator_build_id or --build_id")
+ if not parsed_args.build_target:
+ raise errors.CommandArgError("Must specify --build_target")
if parsed_args.which in [
create_args.CMD_CREATE, CMD_CREATE_CUTTLEFISH, CMD_CREATE_GOLDFISH
@@ -414,7 +417,8 @@
num=args.num,
serial_log_file=args.serial_log_file,
logcat_file=args.logcat_file,
- autoconnect=args.autoconnect)
+ autoconnect=args.autoconnect,
+ branch=args.branch)
elif args.which == CMD_DELETE:
report = device_driver.DeleteAndroidVirtualDevices(
cfg, args.instance_names)
diff --git a/public/actions/create_goldfish_action.py b/public/actions/create_goldfish_action.py
index 688440a..55b089c 100644
--- a/public/actions/create_goldfish_action.py
+++ b/public/actions/create_goldfish_action.py
@@ -19,12 +19,15 @@
emulator.
"""
import logging
+import os
+from acloud.public import errors
from acloud.public.actions import common_operations
from acloud.public.actions import base_device_factory
from acloud.internal.lib import android_build_client
from acloud.internal.lib import auth
from acloud.internal.lib import goldfish_compute_client
+from acloud.internal.lib import utils
logger = logging.getLogger(__name__)
@@ -33,6 +36,11 @@
goldfish_compute_client.GoldfishComputeClient.SCOPE
])
+_EMULATOR_INFO_FILENAME = "emulator-info.txt"
+_EMULATOR_VERSION_PATTERN = "version-emulator"
+_SYSIMAGE_INFO_FILENAME = "android-info.txt"
+_SYSIMAGE_VERSION_PATTERN = "version-sysimage-{}-{}"
+
class GoldfishDeviceFactory(base_device_factory.BaseDeviceFactory):
"""A class that can produce a goldfish device.
@@ -118,6 +126,59 @@
return instance
+def ParseBuildInfo(filename, pattern):
+ """Parse build id based on a substring.
+
+ This will parse a file which contains build information to be used. For an
+ emulator build, the file will contain the information about the corresponding
+ stable system image build id. Similarly, for a system image build, the file
+ will contain the information about the corresponding stable emulator build id.
+ Pattern is a substring being used as a key to parse the build info. For
+ example, "version-sysimage-git_pi-dev-sdk_gphone_x86_64-userdebug".
+
+ Args:
+ filename: Name of file to parse.
+ pattern: Substring to look for in file
+
+ Returns:
+ Build id parsed from the file based on pattern
+ Returns None if pattern not found in file
+ """
+ with open(filename) as build_info_file:
+ for line in build_info_file:
+ if pattern in line:
+ return line.rstrip().split("=")[1]
+ return None
+
+
+def _FetchBuildIdFromFile(cfg, build_target, build_id, pattern, filename):
+ """Parse and fetch build id from a file based on a pattern.
+
+ Verify if one of the system image or emulator binary build id is missing.
+ If found missing, then update according to the resource file.
+
+ Args:
+ cfg: An AcloudConfig instance.
+ build_target: Target name.
+ build_id: Build id, a string, e.g. "2263051", "P2804227"
+ pattern: A string to parse build info file.
+ filename: Name of file containing the build info.
+
+ Returns:
+ A build id or None
+ """
+ build_client = android_build_client.AndroidBuildClient(
+ auth.CreateCredentials(cfg, ALL_SCOPES))
+
+ with utils.TempDir() as tempdir:
+ temp_filename = os.path.join(tempdir, filename)
+ build_client.DownloadArtifact(build_target,
+ build_id,
+ filename,
+ temp_filename)
+
+ return ParseBuildInfo(temp_filename, pattern)
+
def CreateDevices(cfg,
build_target=None,
@@ -127,7 +188,8 @@
num=1,
serial_log_file=None,
logcat_file=None,
- autoconnect=False):
+ autoconnect=False,
+ branch=None):
"""Create one or multiple Goldfish devices.
Args:
@@ -141,10 +203,33 @@
be saved to.
logcat_file: String, A path to a file where logcat logs should be saved.
autoconnect: Boolean, Create ssh tunnel(s) and adb connect after device creation.
+ branch: String, Branch name for system image.
Returns:
A Report instance.
"""
+ if emulator_build_id is None:
+ emulator_build_id = _FetchBuildIdFromFile(cfg,
+ build_target,
+ build_id,
+ _EMULATOR_VERSION_PATTERN,
+ _EMULATOR_INFO_FILENAME)
+
+ if emulator_build_id is None:
+ raise errors.CommandArgError("Emulator build id not found "
+ "in %s" % _EMULATOR_INFO_FILENAME)
+
+ if build_id is None:
+ pattern = _SYSIMAGE_VERSION_PATTERN.format(branch, build_target)
+ build_id = _FetchBuildIdFromFile(cfg,
+ cfg.emulator_build_target,
+ emulator_build_id,
+ pattern,
+ _SYSIMAGE_INFO_FILENAME)
+
+ if build_id is None:
+ raise errors.CommandArgError("Emulator system image build id not found "
+ "in %s" % _SYSIMAGE_INFO_FILENAME)
# TODO: Implement copying files from the instance, including
# the serial log (kernel log), and logcat log files.
# TODO: Implement autoconnect.
diff --git a/public/actions/create_goldfish_action_test.py b/public/actions/create_goldfish_action_test.py
index 70474a0..3b76209 100644
--- a/public/actions/create_goldfish_action_test.py
+++ b/public/actions/create_goldfish_action_test.py
@@ -123,5 +123,112 @@
self.assertEquals(report.command, "create_gf")
self.assertEquals(report.status, "SUCCESS")
+ def testCreateDevicesWithoutBuildId(self):
+ """Test CreateDevices when emulator sys image build id is not provided."""
+ cfg = self._CreateCfg()
+
+ # Mock uuid
+ fake_uuid = mock.MagicMock(hex="1234")
+ self.Patch(uuid, "uuid4", return_value=fake_uuid)
+
+ # Mock compute client methods
+ self.compute_client.GetInstanceIP.return_value = self.IP
+ self.compute_client.GenerateImageName.return_value = self.IMAGE
+ self.compute_client.GenerateInstanceName.return_value = self.INSTANCE
+
+ # Mock build client method
+ self.build_client.GetBranch.side_effect = [
+ self.BRANCH, self.EMULATOR_BRANCH
+ ]
+
+ # Mock _FetchBuildIdFromFile method
+ self.Patch(
+ create_goldfish_action,
+ "_FetchBuildIdFromFile",
+ return_value=self.BUILD_ID)
+
+ # Call CreateDevices
+ report = create_goldfish_action.CreateDevices(
+ cfg,
+ self.BUILD_TARGET,
+ None,
+ self.EMULATOR_BUILD_ID,
+ self.GPU,
+ branch=self.BRANCH)
+
+ # Verify
+ self.compute_client.CreateInstance.assert_called_with(
+ instance=self.INSTANCE,
+ blank_data_disk_size_gb=self.EXTRA_DATA_DISK_GB,
+ image_name=self.GOLDFISH_HOST_IMAGE_NAME,
+ image_project=self.GOLDFISH_HOST_IMAGE_PROJECT,
+ build_target=self.BUILD_TARGET,
+ branch=self.BRANCH,
+ build_id=self.BUILD_ID,
+ emulator_branch=self.EMULATOR_BRANCH,
+ emulator_build_id=self.EMULATOR_BUILD_ID,
+ gpu=self.GPU)
+
+ self.assertEquals(report.data, {
+ "devices": [{
+ "instance_name": self.INSTANCE,
+ "ip": self.IP,
+ },],
+ })
+ self.assertEquals(report.command, "create_gf")
+ self.assertEquals(report.status, "SUCCESS")
+
+ #pylint: disable=invalid-name
+ def testCreateDevicesWithoutEmulatorBuildId(self):
+ """Test CreateDevices when emulator build id is not provided."""
+ cfg = self._CreateCfg()
+
+ # Mock uuid
+ fake_uuid = mock.MagicMock(hex="1234")
+ self.Patch(uuid, "uuid4", return_value=fake_uuid)
+
+ # Mock compute client methods
+ self.compute_client.GetInstanceIP.return_value = self.IP
+ self.compute_client.GenerateImageName.return_value = self.IMAGE
+ self.compute_client.GenerateInstanceName.return_value = self.INSTANCE
+
+ # Mock build client method
+ self.build_client.GetBranch.side_effect = [
+ self.BRANCH, self.EMULATOR_BRANCH
+ ]
+
+ # Mock _FetchBuildIdFromFile method
+ self.Patch(
+ create_goldfish_action,
+ "_FetchBuildIdFromFile",
+ return_value=self.EMULATOR_BUILD_ID)
+
+ # Call CreateDevices
+ report = create_goldfish_action.CreateDevices(
+ cfg, self.BUILD_TARGET, self.BUILD_ID, None, self.GPU)
+
+ # Verify
+ self.compute_client.CreateInstance.assert_called_with(
+ instance=self.INSTANCE,
+ blank_data_disk_size_gb=self.EXTRA_DATA_DISK_GB,
+ image_name=self.GOLDFISH_HOST_IMAGE_NAME,
+ image_project=self.GOLDFISH_HOST_IMAGE_PROJECT,
+ build_target=self.BUILD_TARGET,
+ branch=self.BRANCH,
+ build_id=self.BUILD_ID,
+ emulator_branch=self.EMULATOR_BRANCH,
+ emulator_build_id=self.EMULATOR_BUILD_ID,
+ gpu=self.GPU)
+
+ self.assertEquals(report.data, {
+ "devices": [{
+ "instance_name": self.INSTANCE,
+ "ip": self.IP,
+ },],
+ })
+ self.assertEquals(report.command, "create_gf")
+ self.assertEquals(report.status, "SUCCESS")
+
+
if __name__ == "__main__":
unittest.main()