Cherrypick cl/217729965

Turn on serial log capturing but need to refactor logcat capturing
before turning it on.

Bug: 119614469
Test: acloud create --serial_log_file ./serial.tar.gz
acloud create_cf --{build_id,build_target,branch} ... --serial_log_file ./serial.tar.gz
atest acloud_test
Change-Id: I41463a4b72b0f2f86094c0b7d14879621280baca
diff --git a/internal/lib/utils.py b/internal/lib/utils.py
index a90d8da..e17794c 100755
--- a/internal/lib/utils.py
+++ b/internal/lib/utils.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python
-#
 # Copyright 2016 - The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -45,6 +43,10 @@
 
 SSH_KEYGEN_CMD = ["ssh-keygen", "-t", "rsa", "-b", "4096"]
 SSH_KEYGEN_PUB_CMD = ["ssh-keygen", "-y"]
+SSH_ARGS = ["-o", "UserKnownHostsFile=/dev/null",
+            "-o", "StrictHostKeyChecking=no"]
+SSH_CMD = ["ssh"] + SSH_ARGS
+SCP_CMD = ["scp"] + SSH_ARGS
 DEFAULT_RETRY_BACKOFF_FACTOR = 1
 DEFAULT_SLEEP_MULTIPLIER = 0
 
@@ -284,6 +286,39 @@
             tar.add(src, arcname=arcname)
 
 
+def ScpPullFile(src_file, dst_file, host_name, user_name=None,
+                rsa_key_file=None):
+    """Scp pull file from remote.
+
+    Args:
+        src_file: The source file path to be pulled.
+        dst_file: The destiation file path the file is pulled to.
+        host_name: The device host_name or ip to pull file from.
+        user_name: The user_name for scp session.
+        rsa_key_file: The rsa key file.
+    Raises:
+        errors.DeviceConnectionError if scp failed.
+    """
+    scp_cmd_list = SCP_CMD[:]
+    if rsa_key_file:
+        scp_cmd_list.extend(["-i", rsa_key_file])
+    else:
+        logger.warning(
+            "Rsa key file is not specified. "
+            "Will use default rsa key set in user environment")
+    if user_name:
+        scp_cmd_list.append("%s@%s:%s" % (user_name, host_name, src_file))
+    else:
+        scp_cmd_list.append("%s:%s" % (host_name, src_file))
+    scp_cmd_list.append(dst_file)
+    try:
+        subprocess.check_call(scp_cmd_list)
+    except subprocess.CalledProcessError as e:
+        raise errors.DeviceConnectionError(
+            "Failed to pull file %s from %s with '%s': %s" % (
+                src_file, host_name, " ".join(scp_cmd_list), e))
+
+
 def CreateSshKeyPairIfNotExist(private_key_path, public_key_path):
     """Create the ssh key pair if they don't exist.
 
diff --git a/internal/lib/utils_test.py b/internal/lib/utils_test.py
index d89cd03..9225ddd 100644
--- a/internal/lib/utils_test.py
+++ b/internal/lib/utils_test.py
@@ -29,6 +29,7 @@
 
 from acloud.internal.lib import driver_test_lib
 from acloud.internal.lib import utils
+from acloud.public import errors
 
 # Tkinter may not be supported so mock it out.
 try:
@@ -337,6 +338,43 @@
         self.assertEqual(expected_value, utils.AddUserGroupsToCmd(command,
                                                                   groups))
 
+    @staticmethod
+    def testScpPullFileSuccess():
+        """Test scp pull file successfully."""
+        subprocess.check_call = mock.MagicMock()
+        utils.ScpPullFile("/tmp/test", "/tmp/test_1.log", "192.168.0.1")
+        subprocess.check_call.assert_called_with(utils.SCP_CMD + [
+            "192.168.0.1:/tmp/test", "/tmp/test_1.log"])
+
+    @staticmethod
+    def testScpPullFileWithUserNameSuccess():
+        """Test scp pull file successfully."""
+        subprocess.check_call = mock.MagicMock()
+        utils.ScpPullFile("/tmp/test", "/tmp/test_1.log", "192.168.0.1",
+                          user_name="abc")
+        subprocess.check_call.assert_called_with(utils.SCP_CMD + [
+            "abc@192.168.0.1:/tmp/test", "/tmp/test_1.log"])
+
+    # pylint: disable=invalid-name
+    @staticmethod
+    def testScpPullFileWithUserNameWithRsaKeySuccess():
+        """Test scp pull file successfully."""
+        subprocess.check_call = mock.MagicMock()
+        utils.ScpPullFile("/tmp/test", "/tmp/test_1.log", "192.168.0.1",
+                          user_name="abc", rsa_key_file="/tmp/my_key")
+        subprocess.check_call.assert_called_with(utils.SCP_CMD + [
+            "-i", "/tmp/my_key", "abc@192.168.0.1:/tmp/test",
+            "/tmp/test_1.log"])
+
+    def testScpPullFileScpFailure(self):
+        """Test scp pull file failure."""
+        subprocess.check_call = mock.MagicMock(
+            side_effect=subprocess.CalledProcessError(123, "fake",
+                                                      "fake error"))
+        self.assertRaises(
+            errors.DeviceConnectionError,
+            utils.ScpPullFile, "/tmp/test", "/tmp/test_1.log", "192.168.0.1")
+
 
 if __name__ == "__main__":
     unittest.main()