Merge "Enable deleting instances by specifying --adb-port."
diff --git a/create/avd_spec.py b/create/avd_spec.py
index 039b167..e800b87 100644
--- a/create/avd_spec.py
+++ b/create/avd_spec.py
@@ -107,6 +107,9 @@
         # Reporting args.
         self._serial_log_file = None
         self._logcat_file = None
+        # gpu and emulator_build_id is only used for goldfish avd_type.
+        self._gpu = None
+        self._emulator_build_id = None
 
         self._ProcessArgs(args)
 
@@ -248,6 +251,8 @@
         self._kernel_build_id = args.kernel_build_id
         self._serial_log_file = args.serial_log_file
         self._logcat_file = args.logcat_file
+        self._emulator_build_id = args.emulator_build_id
+        self._gpu = args.gpu
 
     @staticmethod
     def _GetFlavorFromLocalImage(image_path):
@@ -516,3 +521,13 @@
     def logcat_file(self):
         """Return logcat file path."""
         return self._logcat_file
+
+    @property
+    def gpu(self):
+        """Return gpu."""
+        return self._gpu
+
+    @property
+    def emulator_build_id(self):
+        """Return emulator_build_id."""
+        return self._emulator_build_id
diff --git a/create/create.py b/create/create.py
index 011eb8b..0769593 100644
--- a/create/create.py
+++ b/create/create.py
@@ -32,6 +32,7 @@
 from acloud.create import cheeps_remote_image_remote_instance
 from acloud.create import gce_local_image_remote_instance
 from acloud.create import gce_remote_image_remote_instance
+from acloud.create import goldfish_remote_image_remote_instance
 from acloud.create import local_image_local_instance
 from acloud.create import local_image_remote_instance
 from acloud.create import remote_image_remote_instance
@@ -63,6 +64,9 @@
     # Cheeps types
     (constants.TYPE_CHEEPS, constants.IMAGE_SRC_REMOTE, constants.INSTANCE_TYPE_REMOTE):
         cheeps_remote_image_remote_instance.CheepsRemoteImageRemoteInstance,
+    # GF types
+    (constants.TYPE_GF, constants.IMAGE_SRC_REMOTE, constants.INSTANCE_TYPE_REMOTE):
+        goldfish_remote_image_remote_instance.GoldfishRemoteImageRemoteInstance,
 }
 
 
diff --git a/create/create_args.py b/create/create_args.py
index 638e2ea..730682d 100644
--- a/create/create_args.py
+++ b/create/create_args.py
@@ -208,6 +208,24 @@
         choices=constants.SPEC_NAMES,
         help="The name of a pre-configured device spec that we are "
         "going to use.")
+    # Arguments for goldfish type.
+    # TODO(b/118439885): Verify args that are used in wrong avd_type.
+    # e.g. $acloud create --avd-type cuttlefish --emulator-build-id
+    create_parser.add_argument(
+        "--gpu",
+        type=str,
+        dest="gpu",
+        required=False,
+        default=None,
+        help="'goldfish only' GPU accelerator to use if any. "
+        "e.g. nvidia-tesla-k80, omit to use swiftshader")
+    create_parser.add_argument(
+        "--emulator-build-id",
+        type=int,
+        dest="emulator_build_id",
+        required=False,
+        help="'goldfish only' Emulator build used to run the images. "
+        "e.g. 4669466.")
 
     AddCommonCreateArgs(create_parser)
     return create_parser
diff --git a/create/goldfish_remote_image_remote_instance.py b/create/goldfish_remote_image_remote_instance.py
new file mode 100644
index 0000000..592ffaf
--- /dev/null
+++ b/create/goldfish_remote_image_remote_instance.py
@@ -0,0 +1,45 @@
+# Copyright 2019 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+r"""GoldfishRemoteImageRemoteInstance class.
+
+Create class that is responsible for creating a goldfish remote instance AVD
+with a remote image.
+"""
+
+from acloud.create import base_avd_create
+from acloud.internal.lib import utils
+from acloud.public.actions import create_goldfish_action
+
+
+class GoldfishRemoteImageRemoteInstance(base_avd_create.BaseAVDCreate):
+    """Create class for a remote image remote instance AVD."""
+
+    @utils.TimeExecute(function_description="Total time: ",
+                       print_before_call=False, print_status=False)
+    def _CreateAVD(self, avd_spec, no_prompts):
+        """Create the AVD.
+
+        Args:
+            avd_spec: AVDSpec object that tells us what we're going to create.
+            no_prompts: Boolean, True to skip all prompts.
+
+        Returns:
+            A Report instance.
+        """
+        report = create_goldfish_action.CreateDevices(avd_spec=avd_spec)
+
+        # Launch vnc client if we're auto-connecting.
+        if avd_spec.autoconnect:
+            utils.LaunchVNCFromReport(report, avd_spec, no_prompts)
+        return report
diff --git a/internal/lib/goldfish_compute_client.py b/internal/lib/goldfish_compute_client.py
index 4377d6d..be2209c 100644
--- a/internal/lib/goldfish_compute_client.py
+++ b/internal/lib/goldfish_compute_client.py
@@ -43,6 +43,7 @@
 import logging
 
 from acloud import errors
+from acloud.internal import constants
 from acloud.internal.lib import android_compute_client
 from acloud.internal.lib import gcompute_client
 
@@ -132,7 +133,8 @@
                        emulator_branch=None,
                        emulator_build_id=None,
                        blank_data_disk_size_gb=None,
-                       gpu=None):
+                       gpu=None,
+                       avd_spec=None):
         """Create a goldfish instance given a stable host image and a build id.
 
         Args:
@@ -148,6 +150,7 @@
             blank_data_disk_size_gb: Integer, size of the blank data disk in GB.
             gpu: String, GPU that should be attached to the instance, or None of no
                  acceleration is needed. e.g. "nvidia-tesla-k80"
+            avd_spec: An AVDSpec instance.
         """
         self._CheckMachineSize()
 
@@ -161,10 +164,10 @@
         # Goldfish instances are metadata compatible with cuttlefish devices.
         # See details goto/goldfish-deployment
         metadata = self._metadata.copy()
-        resolution = self._resolution.split("x")
+        metadata["user"] = getpass.getuser()
+        metadata[constants.INS_KEY_AVD_TYPE] = constants.TYPE_GF
 
         # Note that we use the same metadata naming conventions as cuttlefish
-        metadata["cvd_01_dpi"] = resolution[3]
         metadata["cvd_01_fetch_android_build_target"] = build_target
         metadata["cvd_01_fetch_android_bid"] = "{branch}/{build_id}".format(
             branch=branch, build_id=build_id)
@@ -173,8 +176,28 @@
                 "cvd_01_fetch_emulator_bid"] = "{branch}/{build_id}".format(
                     branch=emulator_branch, build_id=emulator_build_id)
         metadata["cvd_01_launch"] = "1"
-        metadata["cvd_01_x_res"] = resolution[0]
-        metadata["cvd_01_y_res"] = resolution[1]
+
+        # Update metadata by avd_spec
+        # for legacy create_gf cmd, we will keep using resolution.
+        # And always use avd_spec for acloud create cmd.
+        if avd_spec:
+            metadata[constants.INS_KEY_AVD_FLAVOR] = avd_spec.flavor
+            metadata["cvd_01_x_res"] = avd_spec.hw_property[constants.HW_X_RES]
+            metadata["cvd_01_y_res"] = avd_spec.hw_property[constants.HW_Y_RES]
+            metadata["cvd_01_dpi"] = avd_spec.hw_property[constants.HW_ALIAS_DPI]
+            metadata[constants.INS_KEY_DISPLAY] = ("%sx%s (%s)" % (
+                avd_spec.hw_property[constants.HW_X_RES],
+                avd_spec.hw_property[constants.HW_Y_RES],
+                avd_spec.hw_property[constants.HW_ALIAS_DPI]))
+        else:
+            resolution = self._resolution.split("x")
+            metadata["cvd_01_x_res"] = resolution[0]
+            metadata["cvd_01_y_res"] = resolution[1]
+            metadata["cvd_01_dpi"] = resolution[3]
+
+        # Add labels for giving the instances ability to be filter for
+        # acloud list/delete cmds.
+        labels = {constants.LABEL_CREATE_BY: getpass.getuser()}
 
         # Add per-instance ssh key
         if self._ssh_public_key_path:
@@ -197,4 +220,5 @@
             machine_type=self._machine_type,
             network=self._network,
             zone=self._zone,
-            gpu=gpu)
+            gpu=gpu,
+            labels=labels)
diff --git a/internal/lib/goldfish_compute_client_test.py b/internal/lib/goldfish_compute_client_test.py
index 461fa48..33f16e7 100644
--- a/internal/lib/goldfish_compute_client_test.py
+++ b/internal/lib/goldfish_compute_client_test.py
@@ -86,29 +86,28 @@
                 "fake_arg": "fake_value"
             }])
 
-    def testCreateInstance(self):
+    @mock.patch("getpass.getuser", return_value="fake_user")
+    def testCreateInstance(self, _mock_user):
         """Test CreateInstance."""
 
         expected_metadata = {
-            "cvd_01_dpi":
-            str(self.DPI),
-            "cvd_01_fetch_android_build_target":
-            self.TARGET,
+            "user": "fake_user",
+            "avd_type": "goldfish",
+            "cvd_01_fetch_android_build_target": self.TARGET,
             "cvd_01_fetch_android_bid":
             "{branch}/{build_id}".format(
                 branch=self.BRANCH, build_id=self.BUILD_ID),
             "cvd_01_fetch_emulator_bid":
             "{branch}/{build_id}".format(
                 branch=self.EMULATOR_BRANCH, build_id=self.EMULATOR_BUILD_ID),
-            "cvd_01_launch":
-            "1",
-            "cvd_01_x_res":
-            str(self.X_RES),
-            "cvd_01_y_res":
-            str(self.Y_RES),
+            "cvd_01_launch": "1",
+            "cvd_01_dpi": str(self.DPI),
+            "cvd_01_x_res": str(self.X_RES),
+            "cvd_01_y_res": str(self.Y_RES),
         }
         expected_metadata.update(self.METADATA)
         expected_disk_args = [{"fake_arg": "fake_value"}]
+        expected_labels = {'created_by': "fake_user"}
 
         self.goldfish_compute_client.CreateInstance(
             self.INSTANCE, self.IMAGE, self.IMAGE_PROJECT, self.TARGET,
@@ -126,7 +125,8 @@
             machine_type=self.MACHINE_TYPE,
             network=self.NETWORK,
             zone=self.ZONE,
-            gpu=self.GPU)
+            gpu=self.GPU,
+            labels=expected_labels)
 
 
 if __name__ == "__main__":
diff --git a/public/actions/create_goldfish_action.py b/public/actions/create_goldfish_action.py
index 42e3f0f..8046227 100644
--- a/public/actions/create_goldfish_action.py
+++ b/public/actions/create_goldfish_action.py
@@ -22,8 +22,8 @@
 import os
 
 from acloud import errors
-from acloud.public.actions import common_operations
 from acloud.public.actions import base_device_factory
+from acloud.public.actions import common_operations
 from acloud.internal import constants
 from acloud.internal.lib import android_build_client
 from acloud.internal.lib import auth
@@ -64,7 +64,8 @@
                  build_id,
                  emulator_build_target,
                  emulator_build_id,
-                 gpu=None):
+                 gpu=None,
+                 avd_spec=None):
 
         """Initialize.
 
@@ -75,6 +76,7 @@
             emulator_build_target: String, the emulator build target, e.g. aosp_x86-eng.
             emulator_build_id: String, emulator build id.
             gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80"
+            avd_spec: An AVDSpec instance.
         """
 
         self.credentials = auth.CreateCredentials(cfg)
@@ -90,6 +92,7 @@
         self._emulator_build_id = emulator_build_id
         self._emulator_build_target = emulator_build_target
         self._gpu = gpu
+        self._avd_spec = avd_spec
         self._blank_data_disk_size_gb = cfg.extra_data_disk_size_gb
 
         # Configure clients
@@ -109,7 +112,8 @@
         Returns:
             String, the name of the created instance.
         """
-        instance = self._compute_client.GenerateInstanceName(self._build_id)
+        instance = self._compute_client.GenerateInstanceName(
+            build_id=self._build_id, build_target=self._build_target)
 
         self._compute_client.CreateInstance(
             instance=instance,
@@ -121,7 +125,8 @@
             emulator_branch=self._emulator_branch,
             emulator_build_id=self._emulator_build_id,
             gpu=self._gpu,
-            blank_data_disk_size_gb=self._blank_data_disk_size_gb)
+            blank_data_disk_size_gb=self._blank_data_disk_size_gb,
+            avd_spec=self._avd_spec)
 
         return instance
 
@@ -180,7 +185,8 @@
         return ParseBuildInfo(temp_filename, pattern)
 
 
-def CreateDevices(cfg,
+def CreateDevices(avd_spec=None,
+                  cfg=None,
                   build_target=None,
                   build_id=None,
                   emulator_build_id=None,
@@ -194,6 +200,7 @@
     """Create one or multiple Goldfish devices.
 
     Args:
+        avd_spec: An AVDSpec instance.
         cfg: An AcloudConfig instance.
         build_target: String, the build target, e.g. aosp_x86-eng.
         build_id: String, Build id, e.g. "2263051", "P2804227"
@@ -211,6 +218,19 @@
     Returns:
         A Report instance.
     """
+    if avd_spec:
+        cfg = avd_spec.cfg
+        build_target = avd_spec.remote_image[constants.BUILD_TARGET]
+        build_id = avd_spec.remote_image[constants.BUILD_ID]
+        branch = avd_spec.remote_image[constants.BUILD_BRANCH]
+        num = avd_spec.num
+        emulator_build_id = avd_spec.emulator_build_id
+        gpu = avd_spec.gpu
+        serial_log_file = avd_spec.serial_log_file
+        logcat_file = avd_spec.logcat_file
+        autoconnect = avd_spec.autoconnect
+        report_internal_ip = avd_spec.report_internal_ip
+
     if emulator_build_id is None:
         emulator_build_id = _FetchBuildIdFromFile(cfg,
                                                   build_target,
@@ -242,7 +262,7 @@
 
     device_factory = GoldfishDeviceFactory(cfg, build_target, build_id,
                                            cfg.emulator_build_target,
-                                           emulator_build_id, gpu)
+                                           emulator_build_id, gpu, avd_spec)
 
     return common_operations.CreateDevices(
         command="create_gf",
@@ -250,6 +270,7 @@
         device_factory=device_factory,
         num=num,
         report_internal_ip=report_internal_ip,
+        autoconnect=autoconnect,
         vnc_port=constants.DEFAULT_GOLDFISH_VNC_PORT,
         adb_port=constants.DEFAULT_GOLDFISH_ADB_PORT,
         serial_log_file=serial_log_file,
diff --git a/public/actions/create_goldfish_action_test.py b/public/actions/create_goldfish_action_test.py
index 3d841b1..dc45741 100644
--- a/public/actions/create_goldfish_action_test.py
+++ b/public/actions/create_goldfish_action_test.py
@@ -15,9 +15,10 @@
 # limitations under the License.
 """Tests for acloud.public.actions.create_goldfish_actions."""
 import uuid
-
 import unittest
 import mock
+
+from acloud.internal import constants
 from acloud.internal.lib import android_build_client
 from acloud.internal.lib import android_compute_client
 from acloud.internal.lib import auth
@@ -62,6 +63,16 @@
             "AndroidComputeClient",
             return_value=self.compute_client)
         self.Patch(auth, "CreateCredentials", return_value=mock.MagicMock())
+        #Initialize new avd_spec
+        self.avd_spec = mock.MagicMock()
+        self.avd_spec.cfg = self._CreateCfg()
+        self.avd_spec.remote_image = {constants.BUILD_ID: self.BUILD_ID,
+                                      constants.BUILD_BRANCH: self.BRANCH,
+                                      constants.BUILD_TARGET: self.BUILD_TARGET}
+        self.avd_spec.emulator_build_id = self.EMULATOR_BUILD_ID
+        self.avd_spec.gpu = self.GPU
+        self.avd_spec.serial_log_file = None
+        self.avd_spec.autoconnect = False
 
     def _CreateCfg(self):
         """A helper method that creates a mock configuration object."""
@@ -95,10 +106,12 @@
             self.BRANCH, self.EMULATOR_BRANCH
         ]
 
-        # Call CreateDevices
+        none_avd_spec = None
+
+        # Call CreateDevices with avd_spec is None
         report = create_goldfish_action.CreateDevices(
-            cfg, self.BUILD_TARGET, self.BUILD_ID, self.EMULATOR_BUILD_ID,
-            self.GPU)
+            none_avd_spec, cfg, self.BUILD_TARGET, self.BUILD_ID,
+            self.EMULATOR_BUILD_ID, self.GPU)
 
         # Verify
         self.compute_client.CreateInstance.assert_called_with(
@@ -111,7 +124,8 @@
             build_id=self.BUILD_ID,
             emulator_branch=self.EMULATOR_BRANCH,
             emulator_build_id=self.EMULATOR_BUILD_ID,
-            gpu=self.GPU)
+            gpu=self.GPU,
+            avd_spec=none_avd_spec)
 
         self.assertEquals(report.data, {
             "devices": [
@@ -124,6 +138,25 @@
         self.assertEquals(report.command, "create_gf")
         self.assertEquals(report.status, "SUCCESS")
 
+        # Call CreateDevices with avd_spec
+        self.build_client.GetBranch.side_effect = [
+            self.BRANCH, self.EMULATOR_BRANCH
+        ]
+        report = create_goldfish_action.CreateDevices(avd_spec=self.avd_spec)
+        # 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,
+            avd_spec=self.avd_spec)
+
     def testCreateDevicesWithoutBuildId(self):
         """Test CreateDevices when emulator sys image build id is not provided."""
         cfg = self._CreateCfg()
@@ -148,8 +181,10 @@
             "_FetchBuildIdFromFile",
             return_value=self.BUILD_ID)
 
-        # Call CreateDevices
+        none_avd_spec = None
+        # Call CreateDevices with no avd_spec
         report = create_goldfish_action.CreateDevices(
+            none_avd_spec,
             cfg,
             self.BUILD_TARGET,
             None,
@@ -168,7 +203,8 @@
             build_id=self.BUILD_ID,
             emulator_branch=self.EMULATOR_BRANCH,
             emulator_build_id=self.EMULATOR_BUILD_ID,
-            gpu=self.GPU)
+            gpu=self.GPU,
+            avd_spec=none_avd_spec)
 
         self.assertEquals(report.data, {
             "devices": [{
@@ -179,6 +215,25 @@
         self.assertEquals(report.command, "create_gf")
         self.assertEquals(report.status, "SUCCESS")
 
+        # Call CreateDevices with avd_spec
+        self.build_client.GetBranch.side_effect = [
+            self.BRANCH, self.EMULATOR_BRANCH
+        ]
+        report = create_goldfish_action.CreateDevices(avd_spec=self.avd_spec)
+        # 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,
+            avd_spec=self.avd_spec)
+
     #pylint: disable=invalid-name
     def testCreateDevicesWithoutEmulatorBuildId(self):
         """Test CreateDevices when emulator build id is not provided."""
@@ -204,9 +259,11 @@
             "_FetchBuildIdFromFile",
             return_value=self.EMULATOR_BUILD_ID)
 
+        none_avd_spec = None
         # Call CreateDevices
         report = create_goldfish_action.CreateDevices(
-            cfg, self.BUILD_TARGET, self.BUILD_ID, None, self.GPU)
+            none_avd_spec, cfg, self.BUILD_TARGET, self.BUILD_ID, None,
+            self.GPU)
 
         # Verify
         self.compute_client.CreateInstance.assert_called_with(
@@ -219,7 +276,8 @@
             build_id=self.BUILD_ID,
             emulator_branch=self.EMULATOR_BRANCH,
             emulator_build_id=self.EMULATOR_BUILD_ID,
-            gpu=self.GPU)
+            gpu=self.GPU,
+            avd_spec=none_avd_spec)
 
         self.assertEquals(report.data, {
             "devices": [{
@@ -230,6 +288,25 @@
         self.assertEquals(report.command, "create_gf")
         self.assertEquals(report.status, "SUCCESS")
 
+        # Call CreateDevices with avd_spec
+        self.build_client.GetBranch.side_effect = [
+            self.BRANCH, self.EMULATOR_BRANCH
+        ]
+        report = create_goldfish_action.CreateDevices(avd_spec=self.avd_spec)
+        # 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,
+            avd_spec=self.avd_spec)
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/public/data/default.config b/public/data/default.config
index 702f2fa..53d114f 100644
--- a/public/data/default.config
+++ b/public/data/default.config
@@ -8,6 +8,11 @@
 creds_cache_file: ".acloud_oauth2.dat"
 user_agent: "acloud"
 
+# [GOLDFISH only] The emulator build target: "sdk_tools_linux".
+# We use it to get build id if build id is not provided and It's very unlikely
+# that this will ever change.
+emulator_build_target: "sdk_tools_linux"
+
 default_usr_cfg {
   machine_type: "n1-standard-4"
   network: "default"