CP cl/228564806

Bug: 123894473
Test: atest acloud_test --host
Change-Id: I0cc92681578d33899edc6c276b84cea9cda3e374
diff --git a/create/cheeps_remote_image_remote_instance_test.py b/create/cheeps_remote_image_remote_instance_test.py
index ec2dedf..f01239c 100644
--- a/create/cheeps_remote_image_remote_instance_test.py
+++ b/create/cheeps_remote_image_remote_instance_test.py
@@ -85,6 +85,7 @@
 
         self.assertEquals(report.data, {
             "devices": [{
+                "build_id": self.ANDROID_BUILD_ID,
                 "instance_name": self.INSTANCE,
                 "ip": self.IP.external,
             },],
diff --git a/internal/lib/cvd_compute_client.py b/internal/lib/cvd_compute_client.py
index c146290..2c06b29 100644
--- a/internal/lib/cvd_compute_client.py
+++ b/internal/lib/cvd_compute_client.py
@@ -67,13 +67,13 @@
         Args:
             instance: instance name.
             image_name: A string, the name of the GCE image.
-            image_project: A string, name of the project where the image belongs.
+            image_project: A string, name of the project where the image lives.
                            Assume the default project if None.
             build_target: Target name, e.g. "aosp_cf_x86_phone-userdebug"
             branch: Branch name, e.g. "aosp-master"
             build_id: Build id, a string, e.g. "2263051", "P2804227"
-            kernel_branch: Kernel branch name, e.g. "kernel-android-cf-4.4-x86_64"
-            kernel_build_id: Kernel build id, a string, e.g. "2263051", "P2804227"
+            kernel_branch: Kernel branch name, e.g. "kernel-common-android-4.14"
+            kernel_build_id: Kernel build id, a string, e.g. "223051", "P280427"
             blank_data_disk_size_gb: Size of the blank data disk in GB.
             avd_spec: An AVDSpec instance.
         """
diff --git a/public/acloud_main.py b/public/acloud_main.py
index 25a23e0..2dff3a3 100644
--- a/public/acloud_main.py
+++ b/public/acloud_main.py
@@ -143,13 +143,26 @@
         help="Android branch, e.g. git_master")
     create_cf_parser.add_argument(
         "--kernel_build_id",
+        "--kernel-build-id",
         type=str,
         dest="kernel_build_id",
         required=False,
         help="Android kernel build id, e.g. 4586590. This is to test a new"
-        " kernel build with a particular Android build (--build_id). If not"
-        " specified, the kernel that's bundled with the Android build would"
-        " be used.")
+        " kernel build with a particular Android build (--build_id). If neither"
+        " kernel_branch nor kernel_build_id are specified, the kernel that's"
+        " bundled with the Android build would be used.")
+    create_cf_parser.add_argument(
+        "--kernel_branch",
+        "--kernel-branch",
+        type=str,
+        dest="kernel_branch",
+        required=False,
+        help="Android kernel build branch name, e.g."
+        " kernel-common-android-4.14. This is to test a new kernel build with a"
+        " particular Android build (--build-id). If specified without"
+        " specifying kernel_build_id, the last green build in the branch will"
+        " be used. If neither kernel_branch nor kernel_build_id are specified,"
+        " the kernel that's bundled with the Android build would be used.")
 
     create_args.AddCommonCreateArgs(create_cf_parser)
     subparser_list.append(create_cf_parser)
@@ -351,6 +364,7 @@
             build_target=args.build_target,
             build_id=args.build_id,
             kernel_build_id=args.kernel_build_id,
+            kernel_branch=args.kernel_branch,
             num=args.num,
             serial_log_file=args.serial_log_file,
             logcat_file=args.logcat_file,
diff --git a/public/actions/base_device_factory.py b/public/actions/base_device_factory.py
index 4cfdf65..bd70aad 100644
--- a/public/actions/base_device_factory.py
+++ b/public/actions/base_device_factory.py
@@ -24,6 +24,8 @@
 class BaseDeviceFactory(object):
   """A class that provides basic interface to create a device factory."""
 
+  LATEST = "latest"
+
   def __init__(self, compute_client):
 
     self._compute_client = compute_client
diff --git a/public/actions/common_operations.py b/public/actions/common_operations.py
index af453c9..79e5ef9 100644
--- a/public/actions/common_operations.py
+++ b/public/actions/common_operations.py
@@ -293,6 +293,12 @@
                 "ip": ip,
                 "instance_name": device.instance_name
             }
+            for attr in ("branch", "build_target", "build_id", "kernel_branch",
+                         "kernel_build_target", "kernel_build_id",
+                         "emulator_branch", "emulator_build_target",
+                         "emulator_build_id"):
+                if getattr(device_factory, "_%s" % attr, None):
+                    device_dict[attr] = getattr(device_factory, "_%s" % attr)
             if autoconnect:
                 forwarded_ports = utils.AutoConnect(
                     ip, cfg.ssh_private_key_path,
diff --git a/public/actions/common_operations_test.py b/public/actions/common_operations_test.py
index a234162..030a958 100644
--- a/public/actions/common_operations_test.py
+++ b/public/actions/common_operations_test.py
@@ -37,12 +37,25 @@
     INSTANCE = "fake-instance"
     CMD = "test-cmd"
     AVD_TYPE = "fake-type"
+    BRANCH = "fake-branch"
+    BUILD_TARGET = "fake-target"
+    BUILD_ID = "fake-build-id"
 
+    # pylint: disable=protected-access
     def setUp(self):
         """Set up the test."""
         super(CommonOperationsTest, self).setUp()
         self.build_client = mock.MagicMock()
         self.device_factory = mock.MagicMock()
+        self.device_factory._branch = self.BRANCH
+        self.device_factory._build_target = self.BUILD_TARGET
+        self.device_factory._build_id = self.BUILD_ID
+        self.device_factory._kernel_branch = None
+        self.device_factory._kernel_build_id = None
+        self.device_factory._kernel_build_target = None
+        self.device_factory._emulator_branch = None
+        self.device_factory._emulator_build_id = None
+        self.device_factory._emulator_build_target = None
         self.Patch(
             android_build_client,
             "AndroidBuildClient",
@@ -93,7 +106,10 @@
             _report.data,
             {"devices": [{
                 "ip": self.IP.external,
-                "instance_name": self.INSTANCE
+                "instance_name": self.INSTANCE,
+                "branch": self.BRANCH,
+                "build_id": self.BUILD_ID,
+                "build_target": self.BUILD_TARGET,
             }]})
 
     def testCreateDevicesInternalIP(self):
@@ -109,7 +125,10 @@
             _report.data,
             {"devices": [{
                 "ip": self.IP.internal,
-                "instance_name": self.INSTANCE
+                "instance_name": self.INSTANCE,
+                "branch": self.BRANCH,
+                "build_id": self.BUILD_ID,
+                "build_target": self.BUILD_TARGET,
             }]})
 
 if __name__ == "__main__":
diff --git a/public/actions/create_cuttlefish_action.py b/public/actions/create_cuttlefish_action.py
index 6543976..a07ef5c 100644
--- a/public/actions/create_cuttlefish_action.py
+++ b/public/actions/create_cuttlefish_action.py
@@ -52,7 +52,7 @@
                  "/home/vsoc-01/cuttlefish_runtime/cuttlefish_config.json"]
 
     def __init__(self, cfg, build_target, build_id, kernel_build_id=None,
-                 avd_spec=None):
+                 avd_spec=None, kernel_branch=None):
 
         self.credentials = auth.CreateCredentials(cfg)
 
@@ -67,6 +67,8 @@
         self._kernel_build_id = kernel_build_id
         self._blank_data_disk_size_gb = cfg.extra_data_disk_size_gb
         self._avd_spec = avd_spec
+        self._kernel_branch = kernel_branch
+        self._kernel_build_target = cfg.kernel_build_target
 
         # Configure clients for interaction with GCE/Build servers
         self._build_client = android_build_client.AndroidBuildClient(
@@ -75,9 +77,18 @@
         # Discover branches
         self._branch = self._build_client.GetBranch(build_target, build_id)
         self._kernel_branch = None
-        if kernel_build_id:
+        if kernel_branch:
+            # If no kernel_build_id is given or kernel_build_id is latest,
+            # use the last green build id in the given kernel_branch
+            if not kernel_build_id or kernel_build_id == self.LATEST:
+                self._kernel_build_id = (
+                    self._build_client.GetLKGB(self._kernel_build_target,
+                                               kernel_branch))
+        elif kernel_build_id:
             self._kernel_branch = self._build_client.GetBranch(
-                cfg.kernel_build_target, kernel_build_id)
+                self._kernel_build_target, kernel_build_id)
+        else:
+            self._kernel_build_target = None
 
     def CreateInstance(self):
         """Creates singe configured cuttlefish device.
@@ -120,6 +131,7 @@
                   build_target=None,
                   build_id=None,
                   kernel_build_id=None,
+                  kernel_branch=None,
                   num=1,
                   serial_log_file=None,
                   logcat_file=None,
@@ -133,6 +145,7 @@
         build_target: String, Target name.
         build_id: String, Build id, e.g. "2263051", "P2804227"
         kernel_build_id: String, Kernel build id.
+        kernel_branch: String, Kernel branch name.
         num: Integer, Number of devices to create.
         serial_log_file: String, A path to a tar file where serial output should
                          be saved to.
@@ -161,7 +174,9 @@
         build_id, num, serial_log_file, logcat_file, autoconnect,
         report_internal_ip)
     device_factory = CuttlefishDeviceFactory(cfg, build_target, build_id,
-                                             kernel_build_id, avd_spec)
+                                             avd_spec=avd_spec,
+                                             kernel_build_id=kernel_build_id,
+                                             kernel_branch=kernel_branch)
     return common_operations.CreateDevices("create_cf", cfg, device_factory,
                                            num, constants.TYPE_CF,
                                            report_internal_ip, autoconnect,
diff --git a/public/actions/create_cuttlefish_action_test.py b/public/actions/create_cuttlefish_action_test.py
index 5ec2bd3..fa921f6 100644
--- a/public/actions/create_cuttlefish_action_test.py
+++ b/public/actions/create_cuttlefish_action_test.py
@@ -40,7 +40,9 @@
     IMAGE = "fake-image"
     BUILD_TARGET = "fake-build-target"
     BUILD_ID = "12345"
+    KERNEL_BRANCH = "fake-kernel-branch"
     KERNEL_BUILD_ID = "54321"
+    KERNEL_BUILD_TARGET = "kernel"
     BRANCH = "fake-branch"
     STABLE_HOST_IMAGE_NAME = "fake-stable-host-image-name"
     STABLE_HOST_IMAGE_PROJECT = "fake-stable-host-image-project"
@@ -78,6 +80,7 @@
         cfg.stable_host_image_name = self.STABLE_HOST_IMAGE_NAME
         cfg.stable_host_image_project = self.STABLE_HOST_IMAGE_PROJECT
         cfg.extra_data_disk_size_gb = self.EXTRA_DATA_DISK_GB
+        cfg.kernel_build_target = self.KERNEL_BUILD_TARGET
         return cfg
 
     def testCreateDevices(self):
@@ -94,7 +97,8 @@
         self.compute_client.GenerateInstanceName.return_value = self.INSTANCE
 
         # Mock build client method
-        self.build_client.GetBranch.return_value = self.BRANCH
+        self.build_client.GetBranch.side_effect = [self.BRANCH,
+                                                   self.KERNEL_BRANCH]
 
         # Setup avd_spec as None to use cfg to create devices
         none_avd_spec = None
@@ -111,7 +115,7 @@
             build_target=self.BUILD_TARGET,
             branch=self.BRANCH,
             build_id=self.BUILD_ID,
-            kernel_branch=self.BRANCH,
+            kernel_branch=self.KERNEL_BRANCH,
             kernel_build_id=self.KERNEL_BUILD_ID,
             blank_data_disk_size_gb=self.EXTRA_DATA_DISK_GB,
             avd_spec=none_avd_spec)
@@ -119,6 +123,12 @@
         self.assertEquals(report.data, {
             "devices": [
                 {
+                    "branch": self.BRANCH,
+                    "build_id": self.BUILD_ID,
+                    "build_target": self.BUILD_TARGET,
+                    "kernel_branch": self.KERNEL_BRANCH,
+                    "kernel_build_id": self.KERNEL_BUILD_ID,
+                    "kernel_build_target": self.KERNEL_BUILD_TARGET,
                     "instance_name": self.INSTANCE,
                     "ip": self.IP.external,
                 },
diff --git a/public/actions/create_goldfish_action_test.py b/public/actions/create_goldfish_action_test.py
index dc45741..af8b330 100644
--- a/public/actions/create_goldfish_action_test.py
+++ b/public/actions/create_goldfish_action_test.py
@@ -132,6 +132,12 @@
                 {
                     "instance_name": self.INSTANCE,
                     "ip": self.IP.external,
+                    "branch": self.BRANCH,
+                    "build_id": self.BUILD_ID,
+                    "build_target": self.BUILD_TARGET,
+                    "emulator_branch": self.EMULATOR_BRANCH,
+                    "emulator_build_id": self.EMULATOR_BUILD_ID,
+                    "emulator_build_target": self.EMULATOR_TARGET,
                 },
             ],
         })
@@ -210,6 +216,12 @@
             "devices": [{
                 "instance_name": self.INSTANCE,
                 "ip": self.IP.external,
+                "branch": self.BRANCH,
+                "build_id": self.BUILD_ID,
+                "build_target": self.BUILD_TARGET,
+                "emulator_branch": self.EMULATOR_BRANCH,
+                "emulator_build_id": self.EMULATOR_BUILD_ID,
+                "emulator_build_target": self.EMULATOR_TARGET,
             },],
         })
         self.assertEquals(report.command, "create_gf")
@@ -283,6 +295,12 @@
             "devices": [{
                 "instance_name": self.INSTANCE,
                 "ip": self.IP.external,
+                "branch": self.BRANCH,
+                "build_id": self.BUILD_ID,
+                "build_target": self.BUILD_TARGET,
+                "emulator_branch": self.EMULATOR_BRANCH,
+                "emulator_build_id": self.EMULATOR_BUILD_ID,
+                "emulator_build_target": self.EMULATOR_TARGET,
             },],
         })
         self.assertEquals(report.command, "create_gf")
diff --git a/public/report.py b/public/report.py
index 2ca11da..061cfe6 100755
--- a/public/report.py
+++ b/public/report.py
@@ -159,12 +159,12 @@
             status=self.status,
             errors=self.errors,
             data=self.data)
-        logger.info("Report: %s", json.dumps(result, indent=2))
+        logger.info("Report: %s", json.dumps(result, indent=2, sort_keys=True))
         if not report_file:
             return
         try:
             with open(report_file, "w") as f:
-                json.dump(result, f, indent=2)
+                json.dump(result, f, indent=2, sort_keys=True)
             logger.info("Report file generated at %s",
                         os.path.abspath(report_file))
         except OSError as e: