Clean ssvnc viewer for acloud delete.

Bug: 117474343
Test: m acloud && atest acloud_test && acloud delete
Change-Id: Id59e0c1f1ab723cc0a7640606c433f1790f1dff8
diff --git a/delete/delete.py b/delete/delete.py
index a3b0cce..208ba3b 100644
--- a/delete/delete.py
+++ b/delete/delete.py
@@ -37,6 +37,7 @@
 _COMMAND_GET_PROCESS_ID = ["pgrep", "launch_cvd"]
 _COMMAND_GET_PROCESS_COMMAND = ["ps", "-o", "command", "-p"]
 _RE_LAUNCH_CVD = re.compile(r"^(?P<launch_cvd>.+launch_cvd)(.*--daemon ).+")
+_SSVNC_VIEWER_PATTERN = "vnc://127.0.0.1:%(vnc_port)d"
 
 
 def _GetStopCvd():
@@ -72,6 +73,16 @@
     raise errors.NoExecuteCmd("Cannot find stop_cvd binary.")
 
 
+def CleanupSSVncviewer(vnc_port):
+    """Cleanup the old disconnected ssvnc viewer.
+
+    Args:
+        vnc_port: Integer, port number of vnc.
+    """
+    ssvnc_viewer_pattern = _SSVNC_VIEWER_PATTERN % {"vnc_port":vnc_port}
+    utils.CleanupProcess(ssvnc_viewer_pattern)
+
+
 def DeleteInstances(cfg, instances_to_delete):
     """Delete instances according to instances_to_delete.
 
@@ -82,11 +93,6 @@
     Returns:
         Report instance if there are instances to delete, None otherwise.
     """
-    # TODO(b/117474343): We should do a couple extra things here:
-    #                    - adb disconnect
-    #                    - kill ssh tunnel and ssvnc
-    #                    - give details of each instance
-    #                    - Give better messaging about delete.
     if not instances_to_delete:
         print("No instances to delete")
         return None
@@ -98,6 +104,9 @@
             delete_report = DeleteLocalInstance()
         else:
             remote_instance_list.append(instance.name)
+        # Delete ssvnc viewer
+        if instance.forwarding_vnc_port:
+            CleanupSSVncviewer(instance.forwarding_vnc_port)
 
     if remote_instance_list:
         # TODO(119283708): We should move DeleteAndroidVirtualDevices into
diff --git a/delete/delete_test.py b/delete/delete_test.py
index 8457e4c..38a1112 100644
--- a/delete/delete_test.py
+++ b/delete/delete_test.py
@@ -14,15 +14,18 @@
 """Tests for delete."""
 
 import unittest
+import subprocess
 import mock
 
 from acloud.delete import delete
-
+from acloud.internal.lib import driver_test_lib
+from acloud.internal.lib import utils
 
 # pylint: disable=invalid-name,protected-access,unused-argument,no-member
-class DeleteTest(unittest.TestCase):
+class DeleteTest(driver_test_lib.BaseDriverTest):
     """Test delete functions."""
 
+    # pylint: disable=protected-access
     @mock.patch("os.path.exists", return_value=True)
     @mock.patch("subprocess.check_output")
     def testGetStopcvd(self, mock_subprocess, mock_path_exist):
@@ -49,6 +52,21 @@
         self.assertEquals(delete_report.command, "delete")
         self.assertEquals(delete_report.status, "SUCCESS")
 
+    # pylint: disable=protected-access, no-member
+    def testCleanupSSVncviwer(self):
+        """test cleanup ssvnc viewer."""
+        fake_vnc_port = 9999
+        fake_ss_vncviewer_pattern = delete._SSVNC_VIEWER_PATTERN % {
+            "vnc_port": fake_vnc_port}
+        self.Patch(utils, "IsCommandRunning", return_value=True)
+        self.Patch(subprocess, "check_call", return_value=True)
+        delete.CleanupSSVncviewer(fake_vnc_port)
+        subprocess.check_call.assert_called_with(["pkill", "-9", "-f", fake_ss_vncviewer_pattern])
+
+        subprocess.check_call.call_count = 0
+        self.Patch(utils, "IsCommandRunning", return_value=False)
+        subprocess.check_call.assert_not_called()
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/internal/lib/utils.py b/internal/lib/utils.py
index a2b7448..418d14b 100755
--- a/internal/lib/utils.py
+++ b/internal/lib/utils.py
@@ -62,6 +62,7 @@
 ForwardedPorts = collections.namedtuple("ForwardedPorts", [constants.VNC_PORT,
                                                            constants.ADB_PORT])
 _VNC_BIN = "ssvnc"
+_CMD_KILL = ["pkill", "-9", "-f"]
 _CMD_PGREP = "pgrep"
 _CMD_SG = "sg "
 _CMD_START_VNC = "%(bin)s vnc://127.0.0.1:%(port)d"
@@ -1094,3 +1095,14 @@
     except subprocess.CalledProcessError:
         return None
     return os.path.join(android_build_top, dist_dir.strip())
+
+
+def CleanupProcess(pattern):
+    """Cleanup process with pattern.
+
+    Args:
+        pattern: String, string of process pattern.
+    """
+    if IsCommandRunning(pattern):
+        command_kill = _CMD_KILL + [pattern]
+        subprocess.check_call(command_kill)
diff --git a/reconnect/reconnect.py b/reconnect/reconnect.py
index 5fa3e7f..363f4d9 100644
--- a/reconnect/reconnect.py
+++ b/reconnect/reconnect.py
@@ -26,6 +26,7 @@
 import subprocess
 
 from acloud import errors as root_errors
+from acloud.delete import delete
 from acloud.internal import constants
 from acloud.internal.lib import utils
 from acloud.list import list as list_instance
@@ -35,9 +36,7 @@
 _ADB_DEVICE = "devices"
 _ADB_DISCONNECT = "disconnect"
 _ADB_STATUS_DEVICE = "device"
-_COMMAND_KILLVNC = ["pkill", "-9", "-f"]
 _RE_DISPLAY = re.compile(r"([\d]+)x([\d]+)\s.*")
-_SSVNC_VIEWER_PATTERN = "vnc://127.0.0.1:%(vnc_port)d"
 _VNC_STARTED_PATTERN = "ssvnc vnc://127.0.0.1:%(vnc_port)d"
 
 
@@ -152,17 +151,6 @@
                                    utils.TextColors.FAIL)
 
 
-def CleanupSSVncviewer(ssvnc_viewer_pattern):
-    """Cleanup the old disconnected ssvnc viewer.
-
-    Args:
-        ssvnc_viewer_pattern: String, ssvnc viewer pattern.
-    """
-    if utils.IsCommandRunning(ssvnc_viewer_pattern):
-        command_kill_vnc = _COMMAND_KILLVNC + [ssvnc_viewer_pattern]
-        subprocess.check_call(command_kill_vnc)
-
-
 def StartVnc(vnc_port, display):
     """Start vnc connect to AVD.
 
@@ -177,8 +165,7 @@
     vnc_started_pattern = _VNC_STARTED_PATTERN % {"vnc_port": vnc_port}
     if not utils.IsCommandRunning(vnc_started_pattern):
         #clean old disconnect ssvnc viewer.
-        ssvnc_viewer_pattern = _SSVNC_VIEWER_PATTERN % {"vnc_port": vnc_port}
-        CleanupSSVncviewer(ssvnc_viewer_pattern)
+        delete.CleanupSSVncviewer(vnc_port)
 
         match = _RE_DISPLAY.match(display)
         if match:
diff --git a/reconnect/reconnect_test.py b/reconnect/reconnect_test.py
index f9b047e..7304592 100644
--- a/reconnect/reconnect_test.py
+++ b/reconnect/reconnect_test.py
@@ -112,21 +112,6 @@
         reconnect.StartVnc(vnc_port, display)
         utils.LaunchVncClient.assert_called_with(5555, "888", "777")
 
-    # pylint: disable=protected-access, no-member
-    def testCleanupSSVncviwer(self):
-        """test cleanup ssvnc viewer."""
-        fake_vnc_port = 9999
-        fake_ss_vncviewer_pattern = reconnect._SSVNC_VIEWER_PATTERN % {
-            "vnc_port": fake_vnc_port}
-        self.Patch(utils, "IsCommandRunning", return_value=True)
-        self.Patch(subprocess, "check_call", return_value=True)
-        reconnect.CleanupSSVncviewer(fake_ss_vncviewer_pattern)
-        subprocess.check_call.assert_called_with(['pkill', '-9', '-f', fake_ss_vncviewer_pattern])
-
-        subprocess.check_call.call_count = 0
-        self.Patch(utils, "IsCommandRunning", return_value=False)
-        subprocess.check_call.assert_not_called()
-
 
 class AdbToolsTest(driver_test_lib.BaseDriverTest):
     """Test adb functions."""