Display the summary of acloud reconnect

- Show the devices serial information.
- Update the report of acloud reconnect.

Bug: 122751715
Test: m acloud && atest acloud_test --host &&
acloud reconnect

Change-Id: If2e734c9e92a5ece28acb0bac9226e7572e4bb97
diff --git a/internal/constants.py b/internal/constants.py
index 8ea18b3..b850bbb 100755
--- a/internal/constants.py
+++ b/internal/constants.py
@@ -93,8 +93,10 @@
 # Cuttlefish groups
 LIST_CF_USER_GROUPS = ["kvm", "cvdnetwork"]
 
-ADB_PORT = "adb_port"
+IP = "ip"
+INSTANCE_NAME = "instance_name"
 VNC_PORT = "vnc_port"
+ADB_PORT = "adb_port"
 # For cuttlefish remote instances
 CF_ADB_PORT = 6520
 CF_VNC_PORT = 6444
diff --git a/internal/lib/utils.py b/internal/lib/utils.py
index 3fdb5d6..a1591e9 100755
--- a/internal/lib/utils.py
+++ b/internal/lib/utils.py
@@ -976,9 +976,9 @@
 
 
 def PrintDeviceSummary(report):
-    """Display summary of devices created.
+    """Display summary of devices.
 
-    -Display created device details from the report instance.
+    -Display device details from the report instance.
         report example:
             'data': [{'devices':[{'instance_name': 'ins-f6a397-none-53363',
                                   'ip': u'35.234.10.162'}]}]
@@ -988,7 +988,7 @@
         report: A Report instance.
     """
     PrintColorString("\n")
-    PrintColorString("Device(s) created:")
+    PrintColorString("Device(s) summary:")
     for device in report.data.get("devices", []):
         adb_serial = "(None)"
         adb_port = device.get("adb_port")
diff --git a/reconnect/reconnect.py b/reconnect/reconnect.py
index 152b617..878088c 100644
--- a/reconnect/reconnect.py
+++ b/reconnect/reconnect.py
@@ -26,12 +26,14 @@
 
 from acloud import errors
 from acloud.delete import delete
+from acloud.internal import constants
 from acloud.internal.lib import auth
 from acloud.internal.lib import android_compute_client
 from acloud.internal.lib import utils
 from acloud.internal.lib.adb_tools import AdbTools
 from acloud.list import list as list_instance
 from acloud.public import config
+from acloud.public import report
 
 _RE_DISPLAY = re.compile(r"([\d]+)x([\d]+)\s.*")
 _VNC_STARTED_PATTERN = "ssvnc vnc://127.0.0.1:%(vnc_port)d"
@@ -80,13 +82,21 @@
         instance_name)
 
 
-def ReconnectInstance(ssh_private_key_path, instance):
-    """Reconnect adb/vnc/ssh to the specified instance.
+@utils.TimeExecute(function_description="Reconnect instances")
+def ReconnectInstance(ssh_private_key_path, instance, reconnect_report):
+    """Reconnect to the specified instance.
+
+    It will:
+     - re-establish ssh tunnels for adb/vnc port forwarding
+     - re-establish adb connection
+     - restart vnc client
+     - update device information in reconnect_report
 
     Args:
         ssh_private_key_path: Path to the private key file.
                               e.g. ~/.ssh/acloud_rsa
         instance: list.Instance() object.
+        reconnect_report: Report object.
 
     Raises:
         errors.UnknownAvdType: Unable to reconnect to instance of unknown avd
@@ -99,6 +109,7 @@
 
     adb_cmd = AdbTools(instance.forwarding_adb_port)
     vnc_port = instance.forwarding_vnc_port
+    adb_port = instance.forwarding_adb_port
     # ssh tunnel is up but device is disconnected on adb
     if instance.ssh_tunnel_is_connected and not adb_cmd.IsAdbConnectionAlive():
         adb_cmd.DisconnectAdb()
@@ -113,10 +124,27 @@
             utils.AVD_PORT_DICT[instance.avd_type].adb_port,
             getpass.getuser())
         vnc_port = forwarded_ports.vnc_port
+        adb_port = forwarded_ports.adb_port
 
     if vnc_port:
         StartVnc(vnc_port, instance.display)
 
+    device_dict = {
+        constants.IP: instance.ip,
+        constants.INSTANCE_NAME: instance.name,
+        constants.VNC_PORT: vnc_port,
+        constants.ADB_PORT: adb_port
+    }
+
+    if vnc_port and adb_port:
+        reconnect_report.AddData(key="devices", value=device_dict)
+    else:
+        # We use 'ps aux' to grep adb/vnc fowarding port from ssh tunnel
+        # command. Therefore we report failure here if no vnc_port and
+        # adb_port found.
+        reconnect_report.AddData(key="device_failing_reconnect", value=device_dict)
+        reconnect_report.AddError(instance.name)
+
 
 def Run(args):
     """Run reconnect.
@@ -132,6 +160,8 @@
             cfg, args.instance_names)
     if not instances_to_reconnect:
         instances_to_reconnect = list_instance.ChooseInstances(cfg, args.all)
+
+    reconnect_report = report.Report(command="reconnect")
     for instance in instances_to_reconnect:
         if instance.avd_type not in utils.AVD_PORT_DICT:
             utils.PrintColorString("Skipping reconnect of instance %s due to "
@@ -141,4 +171,6 @@
             continue
         if not instance.islocal:
             AddPublicSshRsaToInstance(cfg, getpass.getuser(), instance.name)
-        ReconnectInstance(cfg.ssh_private_key_path, instance)
+        ReconnectInstance(cfg.ssh_private_key_path, instance, reconnect_report)
+
+    utils.PrintDeviceSummary(reconnect_report)
diff --git a/reconnect/reconnect_test.py b/reconnect/reconnect_test.py
index 2668a71..af8a580 100644
--- a/reconnect/reconnect_test.py
+++ b/reconnect/reconnect_test.py
@@ -38,6 +38,7 @@
     def testReconnectInstance(self):
         """Test Reconnect Instances."""
         ssh_private_key_path = "/fake/acloud_rea"
+        fake_report = mock.MagicMock()
         instance_object = mock.MagicMock()
         instance_object.ip = "1.1.1.1"
         instance_object.islocal = False
@@ -55,13 +56,13 @@
         instance_object.forwarding_vnc_port = 6666
         instance_object.display = ""
         utils.AutoConnect.call_count = 0
-        reconnect.ReconnectInstance(ssh_private_key_path, instance_object)
+        reconnect.ReconnectInstance(ssh_private_key_path, instance_object, fake_report)
         utils.AutoConnect.assert_not_called()
         utils.LaunchVncClient.assert_called_with(6666)
 
         instance_object.display = "888x777 (99)"
         utils.AutoConnect.call_count = 0
-        reconnect.ReconnectInstance(ssh_private_key_path, instance_object)
+        reconnect.ReconnectInstance(ssh_private_key_path, instance_object, fake_report)
         utils.AutoConnect.assert_not_called()
         utils.LaunchVncClient.assert_called_with(6666, "888", "777")
 
@@ -72,7 +73,7 @@
         instance_object.forwarding_vnc_port = 5555
         self.Patch(utils, "AutoConnect",
                    return_value=ForwardedPorts(vnc_port=11111, adb_port=22222))
-        reconnect.ReconnectInstance(ssh_private_key_path, instance_object)
+        reconnect.ReconnectInstance(ssh_private_key_path, instance_object, fake_report)
         utils.AutoConnect.assert_called_with(instance_object.ip,
                                              ssh_private_key_path,
                                              constants.CF_VNC_PORT,
@@ -82,7 +83,7 @@
 
         instance_object.display = "999x777 (99)"
         utils.AutoConnect.call_count = 0
-        reconnect.ReconnectInstance(ssh_private_key_path, instance_object)
+        reconnect.ReconnectInstance(ssh_private_key_path, instance_object, fake_report)
         utils.AutoConnect.assert_called_with(instance_object.ip,
                                              ssh_private_key_path,
                                              constants.CF_VNC_PORT,
@@ -96,13 +97,14 @@
         instance_object.forwarding_vnc_port = 5555
         instance_object.ssh_tunnel_is_connected = False
         utils.AutoConnect.call_count = 0
-        reconnect.ReconnectInstance(ssh_private_key_path, instance_object)
+        reconnect.ReconnectInstance(ssh_private_key_path, instance_object, fake_report)
         utils.AutoConnect.assert_not_called()
         utils.LaunchVncClient.assert_called_with(5555)
 
     def testReconnectInstanceAvdtype(self):
         """Test Reconnect Instances of avd_type."""
         ssh_private_key_path = "/fake/acloud_rea"
+        fake_report = mock.MagicMock()
         instance_object = mock.MagicMock()
         instance_object.ip = "1.1.1.1"
         instance_object.forwarding_vnc_port = 9999
@@ -115,7 +117,7 @@
 
         #test reconnect remote instance when avd_type as gce.
         instance_object.avd_type = "gce"
-        reconnect.ReconnectInstance(ssh_private_key_path, instance_object)
+        reconnect.ReconnectInstance(ssh_private_key_path, instance_object, fake_report)
         utils.AutoConnect.assert_called_with(instance_object.ip,
                                              ssh_private_key_path,
                                              constants.GCE_VNC_PORT,
@@ -124,7 +126,7 @@
 
         #test reconnect remote instance when avd_type as cuttlefish.
         instance_object.avd_type = "cuttlefish"
-        reconnect.ReconnectInstance(ssh_private_key_path, instance_object)
+        reconnect.ReconnectInstance(ssh_private_key_path, instance_object, fake_report)
         utils.AutoConnect.assert_called_with(instance_object.ip,
                                              ssh_private_key_path,
                                              constants.CF_VNC_PORT,
@@ -135,22 +137,26 @@
     def testReconnectInstanceUnknownAvdType(self):
         """Test reconnect instances of unknown avd type."""
         ssh_private_key_path = "/fake/acloud_rea"
+        fake_report = mock.MagicMock()
         instance_object = mock.MagicMock()
         instance_object.avd_type = "unknown"
         self.assertRaises(errors.UnknownAvdType,
                           reconnect.ReconnectInstance,
                           ssh_private_key_path,
-                          instance_object)
+                          instance_object,
+                          fake_report)
 
 
     def testReconnectInstanceNoAvdType(self):
         """Test reconnect instances with no avd type."""
         ssh_private_key_path = "/fake/acloud_rea"
+        fake_report = mock.MagicMock()
         instance_object = mock.MagicMock()
         self.assertRaises(errors.UnknownAvdType,
                           reconnect.ReconnectInstance,
                           ssh_private_key_path,
-                          instance_object)
+                          instance_object,
+                          fake_report)
 
 
     def testStartVnc(self):