Snap for 6258215 from da7716fdad56e66839a9248ec90dfbcd3fcb1649 to r-keystone-qcom-release
Change-Id: I3caededd5028a55ba23d3fc176f846adb104bd59
diff --git a/create/avd_spec.py b/create/avd_spec.py
index fed31a5..b34ad03 100644
--- a/create/avd_spec.py
+++ b/create/avd_spec.py
@@ -26,6 +26,7 @@
import re
import subprocess
import tempfile
+import threading
from acloud import errors
from acloud.create import create_common
@@ -41,7 +42,8 @@
# Default values for build target.
_BRANCH_RE = re.compile(r"^Manifest branch: (?P<branch>.+)")
-_COMMAND_REPO_INFO = ["repo", "info"]
+_COMMAND_REPO_INFO = "repo info platform/tools/acloud"
+_REPO_TIMEOUT = 3
_CF_ZIP_PATTERN = "*img*.zip"
_DEFAULT_BUILD_BITNESS = "x86"
_DEFAULT_BUILD_TYPE = "userdebug"
@@ -88,7 +90,7 @@
# pylint: disable=too-many-public-methods
-class AVDSpec(object):
+class AVDSpec:
"""Class to store data on the type of AVD to create."""
def __init__(self, args):
@@ -575,19 +577,31 @@
Returns:
branch: String, git branch name. e.g. "aosp-master"
"""
- repo_output = ""
- try:
- repo_output = subprocess.check_output(_COMMAND_REPO_INFO)
- except subprocess.CalledProcessError:
- utils.PrintColorString(
- "Unable to determine your repo branch, defaulting to %s"
- % _DEFAULT_BRANCH, utils.TextColors.WARNING)
- for line in repo_output.splitlines():
- match = _BRANCH_RE.match(EscapeAnsi(line))
- if match:
- branch_prefix = _BRANCH_PREFIX.get(self._GetGitRemote(),
- _DEFAULT_BRANCH_PREFIX)
- return branch_prefix + match.group("branch")
+ branch = None
+ # TODO(149460014): Migrate acloud to py3, then remove this
+ # workaround.
+ env = os.environ.copy()
+ env.pop("PYTHONPATH", None)
+ logger.info("Running command \"%s\"", _COMMAND_REPO_INFO)
+ process = subprocess.Popen(_COMMAND_REPO_INFO, shell=True, stdin=None,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, env=env)
+ timer = threading.Timer(_REPO_TIMEOUT, process.kill)
+ timer.start()
+ stdout, _ = process.communicate()
+ if stdout:
+ for line in stdout.splitlines():
+ match = _BRANCH_RE.match(EscapeAnsi(line))
+ if match:
+ branch_prefix = _BRANCH_PREFIX.get(self._GetGitRemote(),
+ _DEFAULT_BRANCH_PREFIX)
+ branch = branch_prefix + match.group("branch")
+ timer.cancel()
+ if branch:
+ return branch
+ utils.PrintColorString(
+ "Unable to determine your repo branch, defaulting to %s"
+ % _DEFAULT_BRANCH, utils.TextColors.WARNING)
return _DEFAULT_BRANCH
def _GetBuildTarget(self, args):
@@ -661,6 +675,14 @@
return self._autoconnect is not False
@property
+ def connect_adb(self):
+ """Auto-connect to adb.
+
+ Return: Boolean, whether autoconnect is enabled.
+ """
+ return self._autoconnect is not False
+
+ @property
def connect_vnc(self):
"""Launch vnc.
@@ -746,6 +768,7 @@
"""Return the Cheeps host image name."""
return self._stable_cheeps_host_image_name
+ # pylint: disable=invalid-name
@property
def stable_cheeps_host_image_project(self):
"""Return the project hosting the Cheeps host image."""
diff --git a/create/avd_spec_test.py b/create/avd_spec_test.py
index 0c323f5..22c1db5 100644
--- a/create/avd_spec_test.py
+++ b/create/avd_spec_test.py
@@ -15,6 +15,7 @@
import glob
import os
+import subprocess
import unittest
import mock
@@ -129,21 +130,32 @@
"/test_path_to_dir/avd-system.tar.gz")
@mock.patch.object(avd_spec.AVDSpec, "_GetGitRemote")
- @mock.patch("subprocess.check_output")
- def testGetBranchFromRepo(self, mock_repo, mock_gitremote):
+ def testGetBranchFromRepo(self, mock_gitremote):
"""Test get branch name from repo info."""
# Check aosp repo gets proper branch prefix.
+ fake_subprocess = mock.MagicMock()
+ fake_subprocess.stdout = mock.MagicMock()
+ fake_subprocess.stdout.readline = mock.MagicMock(return_value='')
+ fake_subprocess.poll = mock.MagicMock(return_value=0)
+ fake_subprocess.returncode = 0
+ return_value = "Manifest branch: master"
+ fake_subprocess.communicate = mock.MagicMock(return_value=(return_value, ''))
+ self.Patch(subprocess, "Popen", return_value=fake_subprocess)
+
mock_gitremote.return_value = "aosp"
- mock_repo.return_value = "Manifest branch: master"
self.assertEqual(self.AvdSpec._GetBranchFromRepo(), "aosp-master")
# Check default repo gets default branch prefix.
mock_gitremote.return_value = ""
- mock_repo.return_value = "Manifest branch: master"
+ return_value = "Manifest branch: master"
+ fake_subprocess.communicate = mock.MagicMock(return_value=(return_value, ''))
+ self.Patch(subprocess, "Popen", return_value=fake_subprocess)
self.assertEqual(self.AvdSpec._GetBranchFromRepo(), "git_master")
# Can't get branch from repo info, set it as default branch.
- mock_repo.return_value = "Manifest branch:"
+ return_value = "Manifest branch:"
+ fake_subprocess.communicate = mock.MagicMock(return_value=(return_value, ''))
+ self.Patch(subprocess, "Popen", return_value=fake_subprocess)
self.assertEqual(self.AvdSpec._GetBranchFromRepo(), "aosp-master")
def testGetBuildBranch(self):
@@ -358,24 +370,28 @@
self.args.autoconnect = False
self.AvdSpec._ProcessMiscArgs(self.args)
self.assertEqual(self.AvdSpec.autoconnect, False)
+ self.assertEqual(self.AvdSpec.connect_adb, False)
self.assertEqual(self.AvdSpec.connect_vnc, False)
self.assertEqual(self.AvdSpec.connect_webrtc, False)
self.args.autoconnect = constants.INS_KEY_VNC
self.AvdSpec._ProcessMiscArgs(self.args)
self.assertEqual(self.AvdSpec.autoconnect, True)
+ self.assertEqual(self.AvdSpec.connect_adb, True)
self.assertEqual(self.AvdSpec.connect_vnc, True)
self.assertEqual(self.AvdSpec.connect_webrtc, False)
self.args.autoconnect = constants.INS_KEY_ADB
self.AvdSpec._ProcessMiscArgs(self.args)
self.assertEqual(self.AvdSpec.autoconnect, True)
+ self.assertEqual(self.AvdSpec.connect_adb, True)
self.assertEqual(self.AvdSpec.connect_vnc, False)
self.assertEqual(self.AvdSpec.connect_webrtc, False)
self.args.autoconnect = constants.INS_KEY_WEBRTC
self.AvdSpec._ProcessMiscArgs(self.args)
self.assertEqual(self.AvdSpec.autoconnect, True)
+ self.assertEqual(self.AvdSpec.connect_adb, True)
self.assertEqual(self.AvdSpec.connect_vnc, False)
self.assertEqual(self.AvdSpec.connect_webrtc, True)
diff --git a/create/local_image_local_instance.py b/create/local_image_local_instance.py
index d40dd5e..0ee5ee8 100644
--- a/create/local_image_local_instance.py
+++ b/create/local_image_local_instance.py
@@ -34,7 +34,6 @@
[CUTTLEFISH_CONFIG_FILE] which is pointing to the runtime cuttlefish json.
"""
-import json
import logging
import os
import shutil
@@ -44,7 +43,6 @@
from acloud import errors
from acloud.create import base_avd_create
-from acloud.delete import delete
from acloud.internal import constants
from acloud.internal.lib import utils
from acloud.internal.lib.adb_tools import AdbTools
@@ -56,15 +54,13 @@
logger = logging.getLogger(__name__)
_CMD_LAUNCH_CVD_ARGS = (" -daemon -cpus %s -x_res %s -y_res %s -dpi %s "
- "-memory_mb %s -system_image_dir %s "
- "-instance_dir %s")
+ "-memory_mb %s -run_adb_connector=%s "
+ "-system_image_dir %s -instance_dir %s")
_CMD_LAUNCH_CVD_DISK_ARGS = (" -blank_data_image_mb %s "
"-data_policy always_create")
_CONFIRM_RELAUNCH = ("\nCuttlefish AVD[id:%d] is already running. \n"
"Enter 'y' to terminate current instance and launch a new "
"instance, enter anything else to exit out[y/N]: ")
-_ENV_CVD_HOME = "HOME"
-_ENV_CUTTLEFISH_INSTANCE = "CUTTLEFISH_INSTANCE"
_LAUNCH_CVD_TIMEOUT_SECS = 120 # default timeout as 120 seconds
_LAUNCH_CVD_TIMEOUT_ERROR = ("Cuttlefish AVD launch timeout, did not complete "
"within %d secs.")
@@ -102,13 +98,13 @@
constants.CMD_LAUNCH_CVD)
cmd = self.PrepareLaunchCVDCmd(launch_cvd_path,
avd_spec.hw_property,
+ avd_spec.connect_adb,
local_image_path,
avd_spec.local_instance_id)
result_report = report.Report(command="create")
instance_name = instance.GetLocalInstanceName(
avd_spec.local_instance_id)
- local_ports = instance.GetLocalPortsbyInsId(avd_spec.local_instance_id)
try:
self.CheckLaunchCVD(
cmd, host_bins_path, avd_spec.local_instance_id, local_image_path,
@@ -116,18 +112,27 @@
except errors.LaunchCVDFail as launch_error:
result_report.SetStatus(report.Status.BOOT_FAIL)
result_report.AddDeviceBootFailure(
- instance_name, constants.LOCALHOST, local_ports.adb_port,
- local_ports.vnc_port, str(launch_error))
+ instance_name, constants.LOCALHOST, None, None,
+ error=str(launch_error))
return result_report
- result_report.SetStatus(report.Status.SUCCESS)
- result_report.AddDevice(instance_name, constants.LOCALHOST,
- local_ports.adb_port, local_ports.vnc_port)
- # Launch vnc client if we're auto-connecting.
- if avd_spec.connect_vnc:
- utils.LaunchVNCFromReport(result_report, avd_spec, no_prompts)
- if avd_spec.unlock_screen:
- AdbTools(local_ports.adb_port).AutoUnlockScreen()
+ active_ins = list_instance.GetActiveCVD(avd_spec.local_instance_id)
+ if active_ins:
+ result_report.SetStatus(report.Status.SUCCESS)
+ result_report.AddDevice(instance_name, constants.LOCALHOST,
+ active_ins.adb_port, active_ins.vnc_port)
+ # Launch vnc client if we're auto-connecting.
+ if avd_spec.connect_vnc:
+ utils.LaunchVNCFromReport(result_report, avd_spec, no_prompts)
+ if avd_spec.unlock_screen:
+ AdbTools(active_ins.adb_port).AutoUnlockScreen()
+ else:
+ err_msg = "cvd_status return non-zero after launch_cvd"
+ logger.error(err_msg)
+ result_report.SetStatus(report.Status.BOOT_FAIL)
+ result_report.AddDeviceBootFailure(
+ instance_name, constants.LOCALHOST, None, None, error=err_msg)
+
return result_report
@staticmethod
@@ -166,8 +171,8 @@
self._FindCvdHostBinaries(avd_spec.local_tool_dirs))
@staticmethod
- def PrepareLaunchCVDCmd(launch_cvd_path, hw_property, system_image_dir,
- local_instance_id):
+ def PrepareLaunchCVDCmd(launch_cvd_path, hw_property, connect_adb,
+ system_image_dir, local_instance_id):
"""Prepare launch_cvd command.
Create the launch_cvd commands with all the required args and add
@@ -177,6 +182,7 @@
launch_cvd_path: String of launch_cvd path.
hw_property: dict object of hw property.
system_image_dir: String of local images path.
+ connect_adb: Boolean flag that enables adb_connector.
local_instance_id: Integer of instance id.
Returns:
@@ -185,7 +191,8 @@
instance_dir = instance.GetLocalInstanceRuntimeDir(local_instance_id)
launch_cvd_w_args = launch_cvd_path + _CMD_LAUNCH_CVD_ARGS % (
hw_property["cpu"], hw_property["x_res"], hw_property["y_res"],
- hw_property["dpi"], hw_property["memory"], system_image_dir,
+ hw_property["dpi"], hw_property["memory"],
+ ("true" if connect_adb else "false"), system_image_dir,
instance_dir)
if constants.HW_ALIAS_DISK in hw_property:
launch_cvd_w_args = (launch_cvd_w_args + _CMD_LAUNCH_CVD_DISK_ARGS %
@@ -218,10 +225,11 @@
# different dir (e.g. downloaded image).
os.environ[constants.ENV_ANDROID_HOST_OUT] = host_bins_path
# Check if the instance with same id is running.
- if self.IsLocalCVDRunning(local_instance_id):
+ existing_ins = list_instance.GetActiveCVD(local_instance_id)
+ if existing_ins:
if no_prompts or utils.GetUserAnswerYes(_CONFIRM_RELAUNCH %
local_instance_id):
- self._StopCvd(host_bins_path, local_instance_id)
+ existing_ins.Delete()
else:
sys.exit(constants.EXIT_BY_USER)
else:
@@ -239,36 +247,6 @@
self._LaunchCvd(cmd, local_instance_id, timeout=timeout_secs)
@staticmethod
- def _StopCvd(host_bins_path, local_instance_id):
- """Execute stop_cvd to stop cuttlefish instance.
-
- Args:
- host_bins_path: String of host package directory.
- local_instance_id: Integer of instance id.
- """
- stop_cvd_cmd = os.path.join(host_bins_path,
- "bin",
- constants.CMD_STOP_CVD)
- with open(os.devnull, "w") as dev_null:
- cvd_env = os.environ.copy()
- cvd_env[constants.ENV_CUTTLEFISH_CONFIG_FILE] = os.path.join(
- instance.GetLocalInstanceRuntimeDir(local_instance_id),
- constants.CUTTLEFISH_CONFIG_FILE)
- subprocess.check_call(
- utils.AddUserGroupsToCmd(
- stop_cvd_cmd, constants.LIST_CF_USER_GROUPS),
- stderr=dev_null, stdout=dev_null, shell=True, env=cvd_env)
-
- # Delete ssvnc viewer
- local_ports = instance.GetLocalPortsbyInsId(local_instance_id)
- delete.CleanupSSVncviewer(local_ports.vnc_port)
- # Disconnect adb device
- adb_cmd = AdbTools(local_ports.adb_port)
- # When relaunch a local instance, we need to pass in retry=True to make
- # sure adb device is completely gone since it will use the same adb port
- adb_cmd.DisconnectAdb(retry=True)
-
- @staticmethod
@utils.TimeExecute(function_description="Waiting for AVD(s) to boot up")
def _LaunchCvd(cmd, local_instance_id, timeout=None):
"""Execute Launch CVD.
@@ -291,8 +269,8 @@
os.makedirs(cvd_runtime_dir)
cvd_env = os.environ.copy()
- cvd_env[_ENV_CVD_HOME] = cvd_home_dir
- cvd_env[_ENV_CUTTLEFISH_INSTANCE] = str(local_instance_id)
+ cvd_env[constants.ENV_CVD_HOME] = cvd_home_dir
+ cvd_env[constants.ENV_CUTTLEFISH_INSTANCE] = str(local_instance_id)
# Check the result of launch_cvd command.
# An exit code of 0 is equivalent to VIRTUAL_DEVICE_BOOT_COMPLETED
process = subprocess.Popen(cmd, shell=True, stderr=subprocess.STDOUT,
@@ -318,19 +296,6 @@
utils.TextColors.WARNING)
@staticmethod
- def IsLocalCVDRunning(local_instance_id):
- """Check if the AVD with specific instance id is running
-
- Args:
- local_instance_id: Integer of instance id.
-
- Return:
- Boolean, True if AVD is running.
- """
- local_ports = instance.GetLocalPortsbyInsId(local_instance_id)
- return AdbTools(local_ports.adb_port).IsAdbConnected()
-
- @staticmethod
def IsLocalImageOccupied(local_image_dir):
"""Check if the given image path is being used by a running CVD process.
@@ -340,15 +305,12 @@
Return:
Integer of instance id which using the same image path.
"""
- local_cvd_ids = list_instance.GetActiveCVDIds()
- for cvd_id in local_cvd_ids:
- cvd_config_path = os.path.join(instance.GetLocalInstanceRuntimeDir(
- cvd_id), constants.CUTTLEFISH_CONFIG_FILE)
- if not os.path.isfile(cvd_config_path):
- continue
- with open(cvd_config_path, "r") as config_file:
- json_array = json.load(config_file)
- for disk_path in json_array[_VIRTUAL_DISK_PATHS]:
+ # TODO(149602560): Remove occupied image checking after after cf disk
+ # overlay is stable
+ for cf_runtime_config_path in instance.GetAllLocalInstanceConfigs():
+ ins = instance.LocalInstance(cf_runtime_config_path)
+ if ins.CvdStatus():
+ for disk_path in ins.virtual_disk_paths:
if local_image_dir in disk_path:
- return cvd_id
+ return ins.instance_id
return None
diff --git a/create/local_image_local_instance_test.py b/create/local_image_local_instance_test.py
index eaa43a5..d1a0acc 100644
--- a/create/local_image_local_instance_test.py
+++ b/create/local_image_local_instance_test.py
@@ -24,6 +24,7 @@
from acloud import errors
from acloud.create import local_image_local_instance
from acloud.list import instance
+from acloud.list import list as list_instance
from acloud.internal import constants
from acloud.internal.lib import driver_test_lib
from acloud.internal.lib import utils
@@ -34,12 +35,12 @@
LAUNCH_CVD_CMD_WITH_DISK = """sg group1 <<EOF
sg group2
-launch_cvd -daemon -cpus fake -x_res fake -y_res fake -dpi fake -memory_mb fake -system_image_dir fake_image_dir -instance_dir fake_cvd_dir -blank_data_image_mb fake -data_policy always_create
+launch_cvd -daemon -cpus fake -x_res fake -y_res fake -dpi fake -memory_mb fake -run_adb_connector=true -system_image_dir fake_image_dir -instance_dir fake_cvd_dir -blank_data_image_mb fake -data_policy always_create
EOF"""
LAUNCH_CVD_CMD_NO_DISK = """sg group1 <<EOF
sg group2
-launch_cvd -daemon -cpus fake -x_res fake -y_res fake -dpi fake -memory_mb fake -system_image_dir fake_image_dir -instance_dir fake_cvd_dir
+launch_cvd -daemon -cpus fake -x_res fake -y_res fake -dpi fake -memory_mb fake -run_adb_connector=true -system_image_dir fake_image_dir -instance_dir fake_cvd_dir
EOF"""
_EXPECTED_DEVICES_IN_REPORT = [
@@ -51,6 +52,13 @@
}
]
+ _EXPECTED_DEVICES_IN_FAILED_REPORT = [
+ {
+ "instance_name": "local-instance-1",
+ "ip": "127.0.0.1"
+ }
+ ]
+
def setUp(self):
"""Initialize new LocalImageLocalInstance."""
super(LocalImageLocalInstanceTest, self).setUp()
@@ -58,7 +66,6 @@
# pylint: disable=protected-access
@mock.patch("acloud.create.local_image_local_instance.utils")
- @mock.patch("acloud.create.local_image_local_instance.instance")
@mock.patch.object(local_image_local_instance.LocalImageLocalInstance,
"PrepareLaunchCVDCmd")
@mock.patch.object(local_image_local_instance.LocalImageLocalInstance,
@@ -66,17 +73,23 @@
@mock.patch.object(local_image_local_instance.LocalImageLocalInstance,
"CheckLaunchCVD")
def testCreateAVD(self, mock_check_launch_cvd, mock_get_image,
- _mock_prepare, mock_instance, mock_utils):
+ _mock_prepare, mock_utils):
"""Test the report returned by _CreateAVD."""
mock_utils.IsSupportedPlatform.return_value = True
-
- mock_instance.GetLocalInstanceName.return_value = "local-instance-1"
- mock_instance.GetLocalPortsbyInsId.return_value = mock.Mock(
- adb_port=6520, vnc_port=6444)
-
mock_get_image.return_value = ("/image/path", "/host/bin/path")
+ mock_avd_spec = mock.Mock(connect_adb=False, unlock_screen=False)
+ self.Patch(instance, "GetLocalInstanceName",
+ return_value="local-instance-1")
+ local_ins = mock.MagicMock(
+ adb_port=6520,
+ vnc_port=6444
+ )
+ local_ins.CvdStatus.return_value = True
+ self.Patch(instance, "LocalInstance",
+ return_value=local_ins)
+ self.Patch(list_instance, "GetActiveCVD",
+ return_value=local_ins)
- mock_avd_spec = mock.Mock(autoconnect=False, unlock_screen=False)
# Success
report = self.local_image_local_instance._CreateAVD(
mock_avd_spec, no_prompts=True)
@@ -90,7 +103,7 @@
mock_avd_spec, no_prompts=True)
self.assertEqual(report.data.get("devices_failing_boot"),
- self._EXPECTED_DEVICES_IN_REPORT)
+ self._EXPECTED_DEVICES_IN_FAILED_REPORT)
self.assertEqual(report.errors, ["timeout"])
# pylint: disable=protected-access
@@ -132,7 +145,7 @@
constants.LIST_CF_USER_GROUPS = ["group1", "group2"]
launch_cmd = self.local_image_local_instance.PrepareLaunchCVDCmd(
- constants.CMD_LAUNCH_CVD, hw_property, "fake_image_dir",
+ constants.CMD_LAUNCH_CVD, hw_property, True, "fake_image_dir",
"fake_cvd_dir")
self.assertEqual(launch_cmd, self.LAUNCH_CVD_CMD_WITH_DISK)
@@ -140,15 +153,14 @@
hw_property = {"cpu": "fake", "x_res": "fake", "y_res": "fake",
"dpi":"fake", "memory": "fake"}
launch_cmd = self.local_image_local_instance.PrepareLaunchCVDCmd(
- constants.CMD_LAUNCH_CVD, hw_property, "fake_image_dir",
+ constants.CMD_LAUNCH_CVD, hw_property, True, "fake_image_dir",
"fake_cvd_dir")
self.assertEqual(launch_cmd, self.LAUNCH_CVD_CMD_NO_DISK)
@mock.patch.object(local_image_local_instance.LocalImageLocalInstance,
"_LaunchCvd")
@mock.patch.object(utils, "GetUserAnswerYes")
- @mock.patch.object(local_image_local_instance.LocalImageLocalInstance,
- "IsLocalCVDRunning")
+ @mock.patch.object(list_instance, "GetActiveCVD")
@mock.patch.object(local_image_local_instance.LocalImageLocalInstance,
"IsLocalImageOccupied")
def testCheckLaunchCVD(self, mock_image_occupied, mock_cvd_running,
@@ -195,8 +207,8 @@
local_instance_id = 3
launch_cvd_cmd = "launch_cvd"
cvd_env = {}
- cvd_env[local_image_local_instance._ENV_CVD_HOME] = "fake_home"
- cvd_env[local_image_local_instance._ENV_CUTTLEFISH_INSTANCE] = str(
+ cvd_env[constants.ENV_CVD_HOME] = "fake_home"
+ cvd_env[constants.ENV_CUTTLEFISH_INSTANCE] = str(
local_instance_id)
process = mock.MagicMock()
process.wait.return_value = True
diff --git a/delete/delete.py b/delete/delete.py
index 86b0a42..02ee484 100644
--- a/delete/delete.py
+++ b/delete/delete.py
@@ -20,7 +20,6 @@
from __future__ import print_function
import logging
-import os
import re
import subprocess
@@ -42,53 +41,9 @@
_COMMAND_GET_PROCESS_ID = ["pgrep", "run_cvd"]
_COMMAND_GET_PROCESS_COMMAND = ["ps", "-o", "command", "-p"]
_RE_RUN_CVD = re.compile(r"^(?P<run_cvd>.+run_cvd)")
-_SSVNC_VIEWER_PATTERN = "vnc://127.0.0.1:%(vnc_port)d"
_LOCAL_INSTANCE_PREFIX = "local-"
-def _GetStopCvd():
- """Get stop_cvd path.
-
- "stop_cvd" and "run_cvd" are in the same folder(host package folder).
- Try to get directory of "run_cvd" by "ps -o command -p <pid>." command.
- For example: "/tmp/bin/run_cvd"
-
- Returns:
- String of stop_cvd file path.
-
- Raises:
- errors.NoExecuteCmd: Can't find stop_cvd.
- """
- process_id = subprocess.check_output(_COMMAND_GET_PROCESS_ID)
- process_info = subprocess.check_output(
- _COMMAND_GET_PROCESS_COMMAND + process_id.splitlines())
- for process in process_info.splitlines():
- match = _RE_RUN_CVD.match(process)
- if match:
- run_cvd_path = match.group("run_cvd")
- stop_cvd_cmd = os.path.join(os.path.dirname(run_cvd_path),
- constants.CMD_STOP_CVD)
- if os.path.exists(stop_cvd_cmd):
- logger.debug("stop_cvd command: %s", stop_cvd_cmd)
- return stop_cvd_cmd
-
- default_stop_cvd = utils.FindExecutable(constants.CMD_STOP_CVD)
- if default_stop_cvd:
- return default_stop_cvd
-
- 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.
@@ -118,8 +73,8 @@
else:
remote_instance_list.append(instance.name)
# Delete ssvnc viewer
- if instance.forwarding_vnc_port:
- CleanupSSVncviewer(instance.forwarding_vnc_port)
+ if instance.vnc_port:
+ utils.CleanupSSVncviewer(instance.vnc_port)
if remote_instance_list:
# TODO(119283708): We should move DeleteAndroidVirtualDevices into
@@ -171,7 +126,7 @@
def DeleteLocalCuttlefishInstance(instance, delete_report):
"""Delete a local cuttlefish instance.
- Delete local instance with stop_cvd command and write delete instance
+ Delete local instance and write delete instance
information to report.
Args:
@@ -182,21 +137,12 @@
delete_report.
"""
try:
- with open(os.devnull, "w") as dev_null:
- cvd_env = os.environ.copy()
- if instance.instance_dir:
- cvd_env[constants.ENV_CUTTLEFISH_CONFIG_FILE] = os.path.join(
- instance.instance_dir, constants.CUTTLEFISH_CONFIG_FILE)
- subprocess.check_call(
- utils.AddUserGroupsToCmd(_GetStopCvd(),
- constants.LIST_CF_USER_GROUPS),
- stderr=dev_null, stdout=dev_null, shell=True, env=cvd_env)
- delete_report.SetStatus(report.Status.SUCCESS)
- device_driver.AddDeletionResultToReport(
- delete_report, [instance.name], failed=[],
- error_msgs=[],
- resource_name="instance")
- CleanupSSVncviewer(instance.vnc_port)
+ instance.Delete()
+ delete_report.SetStatus(report.Status.SUCCESS)
+ device_driver.AddDeletionResultToReport(
+ delete_report, [instance.name], failed=[],
+ error_msgs=[],
+ resource_name="instance")
except subprocess.CalledProcessError as e:
delete_report.AddError(str(e))
delete_report.SetStatus(report.Status.FAIL)
diff --git a/delete/delete_test.py b/delete/delete_test.py
index 49cef1c..829ab7b 100644
--- a/delete/delete_test.py
+++ b/delete/delete_test.py
@@ -14,12 +14,10 @@
"""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
from acloud.list import list as list_instances
from acloud.public import report
@@ -28,20 +26,8 @@
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):
- """Test _GetStopCvd."""
- mock_subprocess.side_effect = ["fake_id",
- "/tmp/bin/run_cvd"]
- expected_value = "/tmp/bin/stop_cvd"
- self.assertEqual(expected_value, delete._GetStopCvd())
-
- @mock.patch.object(delete, "_GetStopCvd", return_value="")
@mock.patch("subprocess.check_call")
- def testDeleteLocalCuttlefishInstance(self, mock_subprocess,
- mock_get_stopcvd):
+ def testDeleteLocalCuttlefishInstance(self, mock_subprocess):
"""Test DeleteLocalCuttlefishInstance."""
mock_subprocess.return_value = True
instance_object = mock.MagicMock()
@@ -108,21 +94,6 @@
self.assertTrue(len(delete_report.errors) > 0)
self.assertEqual(delete_report.status, "FAIL")
- # 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()
-
@mock.patch.object(delete, "DeleteInstances", return_value="")
@mock.patch.object(delete, "DeleteRemoteInstances", return_value="")
def testDeleteInstanceByNames(self, mock_delete_remote_ins,
diff --git a/internal/constants.py b/internal/constants.py
index fb95d44..c530244 100755
--- a/internal/constants.py
+++ b/internal/constants.py
@@ -150,6 +150,8 @@
INS_STATUS_RUNNING = "RUNNING"
LOCAL_INS_NAME = "local-instance"
ENV_CUTTLEFISH_CONFIG_FILE = "CUTTLEFISH_CONFIG_FILE"
+ENV_CUTTLEFISH_INSTANCE = "CUTTLEFISH_INSTANCE"
+ENV_CVD_HOME = "HOME"
CUTTLEFISH_CONFIG_FILE = "cuttlefish_config.json"
TEMP_ARTIFACTS_FOLDER = "acloud_image_artifacts"
diff --git a/internal/lib/cvd_runtime_config.py b/internal/lib/cvd_runtime_config.py
index f15af2e..fd5bba6 100644
--- a/internal/lib/cvd_runtime_config.py
+++ b/internal/lib/cvd_runtime_config.py
@@ -75,16 +75,16 @@
"memory_mb" : 4096,
"cpus" : 2,
"dpi" : 320,
- "virtual_disk_paths" :
- [
- "/path-to-image"
- ],
"instances" :
{
"1" :
{
"adb_ip_and_port" : "127.0.0.1:6520",
"instance_dir" : "/path-to-instance-dir",
+ "virtual_disk_paths" :
+ [
+ "/path-to-image"
+ ],
}
}
}
@@ -101,8 +101,6 @@
adb_connector = self._config_dict.get(_CFG_KEY_ADB_CONNECTOR_BINARY)
self._cvd_tools_path = (os.path.dirname(adb_connector)
if adb_connector else None)
- self._virtual_disk_paths = self._config_dict.get(
- _CFG_KEY_VIRTUAL_DISK_PATHS)
# Below properties will be collected inside of instance id node if there
# are more than one instance.
@@ -110,6 +108,8 @@
self._vnc_port = self._config_dict.get(_CFG_KEY_VNC_PORT)
self._adb_port = self._config_dict.get(_CFG_KEY_ADB_PORT)
self._adb_ip_port = self._config_dict.get(_CFG_KEY_ADB_IP_PORT)
+ self._virtual_disk_paths = self._config_dict.get(
+ _CFG_KEY_VIRTUAL_DISK_PATHS)
if not self._instance_dir:
ins_cfg = self._config_dict.get(_CFG_KEY_INSTANCES)
ins_dict = ins_cfg.get(self._instance_id)
@@ -121,6 +121,7 @@
self._vnc_port = ins_dict.get(_CFG_KEY_VNC_PORT)
self._adb_port = ins_dict.get(_CFG_KEY_ADB_PORT)
self._adb_ip_port = ins_dict.get(_CFG_KEY_ADB_IP_PORT)
+ self._virtual_disk_paths = ins_dict.get(_CFG_KEY_VIRTUAL_DISK_PATHS)
@staticmethod
def _GetCuttlefishRuntimeConfig(runtime_cf_config_path):
diff --git a/internal/lib/utils.py b/internal/lib/utils.py
index e6790d3..a60c6c3 100755
--- a/internal/lib/utils.py
+++ b/internal/lib/utils.py
@@ -100,6 +100,7 @@
# dict of supported system and their distributions.
_SUPPORTED_SYSTEMS_AND_DISTS = {"Linux": ["Ubuntu", "Debian"]}
_DEFAULT_TIMEOUT_ERR = "Function did not complete within %d secs."
+_SSVNC_VIEWER_PATTERN = "vnc://127.0.0.1:%(vnc_port)d"
class TempDir(object):
@@ -917,14 +918,22 @@
Args:
report: Report object, that stores and generates report.
"""
+ PrintColorString("(This is an experimental project for webrtc, and since "
+ "the certificate is self-signed, Chrome will mark it as "
+ "an insecure website. keep going.)",
+ TextColors.WARNING)
+
for device in report.data.get("devices", []):
if device.get("ip"):
- PrintColorString("(This is an experimental project for webrtc, and "
- "since the certificate is self-signed, Chrome will "
- "mark it as an insecure website. keep going.)",
- TextColors.WARNING)
- webbrowser.open_new_tab("%s%s:%s" % (
- _WEBRTC_URL, device.get("ip"), _WEBRTC_PORT))
+ webrtc_link = "%s%s:%s" % (_WEBRTC_URL, device.get("ip"),
+ _WEBRTC_PORT)
+ if os.environ.get(_ENV_DISPLAY, None):
+ webbrowser.open_new_tab(webrtc_link)
+ else:
+ PrintColorString("Remote terminal can't support launch webbrowser.",
+ TextColors.FAIL)
+ PrintColorString("Open %s to remotely control AVD on the "
+ "browser." % webrtc_link)
else:
PrintColorString("Auto-launch devices webrtc in browser failed!",
TextColors.FAIL)
@@ -1251,3 +1260,13 @@
"""
return (namedtuple_object.__dict__.items() if six.PY2
else namedtuple_object._asdict().items())
+
+
+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}
+ CleanupProcess(ssvnc_viewer_pattern)
diff --git a/internal/lib/utils_test.py b/internal/lib/utils_test.py
index 480b4d2..169a36d 100644
--- a/internal/lib/utils_test.py
+++ b/internal/lib/utils_test.py
@@ -411,6 +411,22 @@
first_call_args = utils._ExecuteCommand.call_args_list[0][0]
self.assertEqual(first_call_args[1], args_list)
+ # pylint: disable=protected-access, no-member
+ def testCleanupSSVncviwer(self):
+ """test cleanup ssvnc viewer."""
+ fake_vnc_port = 9999
+ fake_ss_vncviewer_pattern = utils._SSVNC_VIEWER_PATTERN % {
+ "vnc_port": fake_vnc_port}
+ self.Patch(utils, "IsCommandRunning", return_value=True)
+ self.Patch(subprocess, "check_call", return_value=True)
+ utils.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)
+ utils.CleanupSSVncviewer(fake_vnc_port)
+ subprocess.check_call.assert_not_called()
+
if __name__ == "__main__":
unittest.main()
diff --git a/list/instance.py b/list/instance.py
index 8f91245..fb98960 100644
--- a/list/instance.py
+++ b/list/instance.py
@@ -48,6 +48,7 @@
_ACLOUD_CVD_TEMP = os.path.join(tempfile.gettempdir(), "acloud_cvd_temp")
_CVD_RUNTIME_FOLDER_NAME = "cuttlefish_runtime"
+_CVD_STATUS_BIN = "cvd_status"
_MSG_UNABLE_TO_CALCULATE = "Unable to calculate"
_RE_GROUP_ADB = "local_adb_port"
_RE_GROUP_VNC = "local_vnc_port"
@@ -63,10 +64,21 @@
_LOCAL_ZONE = "local"
_FULL_NAME_STRING = ("device serial: %(device_serial)s (%(instance_name)s) "
"elapsed time: %(elapsed_time)s")
+_INDENT = " " * 3
LocalPorts = collections.namedtuple("LocalPorts", [constants.VNC_PORT,
constants.ADB_PORT])
+def GetDefaultCuttlefishConfig():
+ """Get the path of default cuttlefish instance config.
+
+ Return:
+ String, path of cf runtime config.
+ """
+ return os.path.join(os.path.expanduser("~"), _CVD_RUNTIME_FOLDER_NAME,
+ constants.CUTTLEFISH_CONFIG_FILE)
+
+
def GetLocalInstanceName(local_instance_id):
"""Get local cuttlefish instance name by instance id.
@@ -79,8 +91,48 @@
return "%s-%d" % (constants.LOCAL_INS_NAME, local_instance_id)
+def GetLocalInstanceConfig(local_instance_id):
+ """Get the path of instance config.
+
+ Args:
+ local_instance_id: Integer of instance id.
+
+ Return:
+ String, path of cf runtime config.
+ """
+ cfg_path = os.path.join(GetLocalInstanceRuntimeDir(local_instance_id),
+ constants.CUTTLEFISH_CONFIG_FILE)
+ if os.path.isfile(cfg_path):
+ return cfg_path
+ return None
+
+
+def GetAllLocalInstanceConfigs():
+ """Get the list of instance config.
+
+ Return:
+ List of instance config path.
+ """
+ cfg_list = []
+ # Check if any instance config is under home folder.
+ cfg_path = GetDefaultCuttlefishConfig()
+ if os.path.isfile(cfg_path):
+ cfg_list.append(cfg_path)
+
+ # Check if any instance config is under acloud cvd temp folder.
+ if os.path.exists(_ACLOUD_CVD_TEMP):
+ for ins_name in os.listdir(_ACLOUD_CVD_TEMP):
+ cfg_path = os.path.join(_ACLOUD_CVD_TEMP,
+ ins_name,
+ _CVD_RUNTIME_FOLDER_NAME,
+ constants.CUTTLEFISH_CONFIG_FILE)
+ if os.path.isfile(cfg_path):
+ cfg_list.append(cfg_path)
+ return cfg_list
+
+
def GetLocalInstanceHomeDir(local_instance_id):
- """Get local instance home dir accroding to instance id.
+ """Get local instance home dir according to instance id.
Args:
local_instance_id: Integer of instance id.
@@ -105,34 +157,6 @@
_CVD_RUNTIME_FOLDER_NAME)
-def GetCuttlefishRuntimeConfig(local_instance_id):
- """Get and parse cuttlefish_config.json.
-
- Args:
- local_instance_id: Integer of instance id.
-
- Returns:
- A CvdRuntimeConfig instance.
- """
- runtime_cf_config_path = os.path.join(GetLocalInstanceRuntimeDir(
- local_instance_id), constants.CUTTLEFISH_CONFIG_FILE)
- return cvd_runtime_config.CvdRuntimeConfig(runtime_cf_config_path)
-
-
-def GetLocalPortsbyInsId(local_instance_id):
- """Get vnc and adb port by local instance id.
-
- Args:
- local_instance_id: local_instance_id: Integer of instance id.
-
- Returns:
- NamedTuple of (vnc_port, adb_port) used by local instance, both are
- integers.
- """
- return LocalPorts(vnc_port=constants.CF_VNC_PORT + local_instance_id - 1,
- adb_port=constants.CF_ADB_PORT + local_instance_id - 1)
-
-
def _GetCurrentLocalTime():
"""Return a datetime object for current time in local time zone."""
return datetime.datetime.now(dateutil.tz.tzlocal())
@@ -193,31 +217,30 @@
def Summary(self):
"""Let's make it easy to see what this class is holding."""
- indent = " " * 3
representation = []
representation.append(" name: %s" % self._name)
- representation.append("%s IP: %s" % (indent, self._ip))
- representation.append("%s create time: %s" % (indent, self._createtime))
- representation.append("%s elapse time: %s" % (indent, self._elapsed_time))
- representation.append("%s status: %s" % (indent, self._status))
- representation.append("%s avd type: %s" % (indent, self._avd_type))
- representation.append("%s display: %s" % (indent, self._display))
- representation.append("%s vnc: 127.0.0.1:%s" % (indent, self._vnc_port))
- representation.append("%s zone: %s" % (indent, self._zone))
+ representation.append("%s IP: %s" % (_INDENT, self._ip))
+ representation.append("%s create time: %s" % (_INDENT, self._createtime))
+ representation.append("%s elapse time: %s" % (_INDENT, self._elapsed_time))
+ representation.append("%s status: %s" % (_INDENT, self._status))
+ representation.append("%s avd type: %s" % (_INDENT, self._avd_type))
+ representation.append("%s display: %s" % (_INDENT, self._display))
+ representation.append("%s vnc: 127.0.0.1:%s" % (_INDENT, self._vnc_port))
+ representation.append("%s zone: %s" % (_INDENT, self._zone))
- if self._adb_port:
+ if self._adb_port and self._device_information:
representation.append("%s adb serial: 127.0.0.1:%s" %
- (indent, self._adb_port))
+ (_INDENT, self._adb_port))
representation.append("%s product: %s" % (
- indent, self._device_information["product"]))
+ _INDENT, self._device_information["product"]))
representation.append("%s model: %s" % (
- indent, self._device_information["model"]))
+ _INDENT, self._device_information["model"]))
representation.append("%s device: %s" % (
- indent, self._device_information["device"]))
+ _INDENT, self._device_information["device"]))
representation.append("%s transport_id: %s" % (
- indent, self._device_information["transport_id"]))
+ _INDENT, self._device_information["transport_id"]))
else:
- representation.append("%s adb serial: disconnected" % indent)
+ representation.append("%s adb serial: disconnected" % _INDENT)
return "\n".join(representation)
@@ -247,16 +270,6 @@
return self._display
@property
- def forwarding_adb_port(self):
- """Return the adb port."""
- return self._adb_port
-
- @property
- def forwarding_vnc_port(self):
- """Return the vnc port."""
- return self._vnc_port
-
- @property
def ssh_tunnel_is_connected(self):
"""Return the connect status."""
return self._ssh_tunnel_is_connected
@@ -299,25 +312,28 @@
class LocalInstance(Instance):
"""Class to store data of local cuttlefish instance."""
-
- def __init__(self, local_instance_id, cf_runtime_cfg):
+ def __init__(self, cf_config_path):
"""Initialize a localInstance object.
Args:
- local_instance_id: Integer of instance id.
- cf_runtime_cfg: A CvdRuntimeConfig instance.
+ cf_config_path: String, path to the cf runtime config.
"""
- display = _DISPLAY_STRING % {"x_res": cf_runtime_cfg.x_res,
- "y_res": cf_runtime_cfg.y_res,
- "dpi": cf_runtime_cfg.dpi}
+ self._cf_runtime_cfg = cvd_runtime_config.CvdRuntimeConfig(cf_config_path)
+ self._instance_dir = self._cf_runtime_cfg.instance_dir
+ self._virtual_disk_paths = self._cf_runtime_cfg.virtual_disk_paths
+ self._local_instance_id = int(self._cf_runtime_cfg.instance_id)
+
+ display = _DISPLAY_STRING % {"x_res": self._cf_runtime_cfg.x_res,
+ "y_res": self._cf_runtime_cfg.y_res,
+ "dpi": self._cf_runtime_cfg.dpi}
# TODO(143063678), there's no createtime info in
# cuttlefish_config.json so far.
- name = GetLocalInstanceName(local_instance_id)
+ name = GetLocalInstanceName(self._local_instance_id)
fullname = (_FULL_NAME_STRING %
- {"device_serial": "127.0.0.1:%d" % cf_runtime_cfg.adb_port,
+ {"device_serial": "127.0.0.1:%s" % self._cf_runtime_cfg.adb_port,
"instance_name": name,
"elapsed_time": None})
- adb_device = AdbTools(cf_runtime_cfg.adb_port)
+ adb_device = AdbTools(self._cf_runtime_cfg.adb_port)
device_information = None
if adb_device.IsAdbConnected():
device_information = adb_device.device_information
@@ -325,19 +341,102 @@
super(LocalInstance, self).__init__(
name=name, fullname=fullname, display=display, ip="127.0.0.1",
status=constants.INS_STATUS_RUNNING,
- adb_port=cf_runtime_cfg.adb_port, vnc_port=cf_runtime_cfg.vnc_port,
+ adb_port=self._cf_runtime_cfg.adb_port,
+ vnc_port=self._cf_runtime_cfg.vnc_port,
createtime=None, elapsed_time=None, avd_type=constants.TYPE_CF,
is_local=True, device_information=device_information,
zone=_LOCAL_ZONE)
- # LocalInstance class properties
- self._instance_dir = cf_runtime_cfg.instance_dir
+ def Summary(self):
+ """Return the string that this class is holding."""
+ instance_home = "%s instance home: %s" % (_INDENT, self._instance_dir)
+ return "%s\n%s" % (super(LocalInstance, self).Summary(), instance_home)
+
+ def CvdStatus(self):
+ """check if local instance is active.
+
+ Execute cvd_status cmd to check if it exit without error.
+
+ Returns
+ True if instance is active.
+ """
+ cvd_env = os.environ.copy()
+ cvd_env[constants.ENV_CUTTLEFISH_CONFIG_FILE] = self._cf_runtime_cfg.config_path
+ cvd_env[constants.ENV_CVD_HOME] = GetLocalInstanceHomeDir(self._local_instance_id)
+ cvd_env[constants.ENV_CUTTLEFISH_INSTANCE] = str(self._local_instance_id)
+ try:
+ cvd_status_cmd = os.path.join(self._cf_runtime_cfg.cvd_tools_path,
+ _CVD_STATUS_BIN)
+ logger.debug("Running cmd[%s] to check cvd status.", cvd_status_cmd)
+ process = subprocess.Popen(cvd_status_cmd,
+ stdin=None,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ env=cvd_env)
+ stdout, _ = process.communicate()
+ if process.returncode != 0:
+ if stdout:
+ logger.debug("Local instance[%s] is not active: %s",
+ self.name, stdout.strip())
+ return False
+ return True
+ except subprocess.CalledProcessError as cpe:
+ logger.error("Failed to run cvd_status: %s", cpe.output)
+ return False
+
+ def Delete(self):
+ """Execute stop_cvd to stop local cuttlefish instance.
+
+ - We should get the same host tool used to launch cvd to delete instance
+ , So get stop_cvd bin from the cvd runtime config.
+ - Add CUTTLEFISH_CONFIG_FILE env variable to tell stop_cvd which cvd
+ need to be deleted.
+ - Stop adb since local instance use the fixed adb port and could be
+ reused again soon.
+ """
+ stop_cvd_cmd = os.path.join(self.cf_runtime_cfg.cvd_tools_path,
+ constants.CMD_STOP_CVD)
+ logger.debug("Running cmd[%s] to delete local cvd", stop_cvd_cmd)
+ with open(os.devnull, "w") as dev_null:
+ cvd_env = os.environ.copy()
+ if self.instance_dir:
+ cvd_env[constants.ENV_CUTTLEFISH_CONFIG_FILE] = self._cf_runtime_cfg.config_path
+ cvd_env[constants.ENV_CVD_HOME] = GetLocalInstanceHomeDir(
+ self._local_instance_id)
+ cvd_env[constants.ENV_CUTTLEFISH_INSTANCE] = str(self._local_instance_id)
+ else:
+ logger.error("instance_dir is null!! instance[%d] might not be"
+ " deleted", self._local_instance_id)
+ subprocess.check_call(
+ utils.AddUserGroupsToCmd(stop_cvd_cmd,
+ constants.LIST_CF_USER_GROUPS),
+ stderr=dev_null, stdout=dev_null, shell=True, env=cvd_env)
+
+ adb_cmd = AdbTools(self.adb_port)
+ # When relaunch a local instance, we need to pass in retry=True to make
+ # sure adb device is completely gone since it will use the same adb port
+ adb_cmd.DisconnectAdb(retry=True)
@property
def instance_dir(self):
"""Return _instance_dir."""
return self._instance_dir
+ @property
+ def instance_id(self):
+ """Return _local_instance_id."""
+ return self._local_instance_id
+
+ @property
+ def virtual_disk_paths(self):
+ """Return virtual_disk_paths"""
+ return self._virtual_disk_paths
+
+ @property
+ def cf_runtime_cfg(self):
+ """Return _cf_runtime_cfg"""
+ return self._cf_runtime_cfg
+
class LocalGoldfishInstance(Instance):
"""Class to store data of local goldfish instance."""
diff --git a/list/instance_test.py b/list/instance_test.py
index 9832c80..a2c7922 100644
--- a/list/instance_test.py
+++ b/list/instance_test.py
@@ -27,6 +27,7 @@
import dateutil.tz
from acloud.internal import constants
+from acloud.internal.lib import cvd_runtime_config
from acloud.internal.lib import driver_test_lib
from acloud.internal.lib.adb_tools import AdbTools
from acloud.list import instance
@@ -62,15 +63,18 @@
""""Test get local instance info from launch_cvd process."""
self.Patch(subprocess, "check_output", return_value=self.PS_LAUNCH_CVD)
cf_config = mock.MagicMock(
+ instance_id=2,
x_res=1080,
y_res=1920,
dpi=480,
instance_dir="fake_instance_dir",
adb_port=6521,
- vnc_port=6445
+ vnc_port=6445,
+ adb_ip_port="127.0.0.1:6521",
)
-
- local_instance = instance.LocalInstance(2, cf_config)
+ self.Patch(cvd_runtime_config, "CvdRuntimeConfig",
+ return_value=cf_config)
+ local_instance = instance.LocalInstance(cf_config)
self.assertEqual(constants.LOCAL_INS_NAME + "-2", local_instance.name)
self.assertEqual(True, local_instance.islocal)
@@ -80,8 +84,8 @@
constants.LOCAL_INS_NAME + "-2",
"None"))
self.assertEqual(expected_full_name, local_instance.fullname)
- self.assertEqual(6521, local_instance.forwarding_adb_port)
- self.assertEqual(6445, local_instance.forwarding_vnc_port)
+ self.assertEqual(6521, local_instance.adb_port)
+ self.assertEqual(6445, local_instance.vnc_port)
@mock.patch("acloud.list.instance.tempfile")
@mock.patch("acloud.list.instance.AdbTools")
@@ -214,8 +218,8 @@
# test ssh_tunnel_is_connected will be true if ssh tunnel connection is found
instance_info = instance.RemoteInstance(self.GCE_INSTANCE)
self.assertTrue(instance_info.ssh_tunnel_is_connected)
- self.assertEqual(instance_info.forwarding_adb_port, fake_adb)
- self.assertEqual(instance_info.forwarding_vnc_port, fake_vnc)
+ self.assertEqual(instance_info.adb_port, fake_adb)
+ self.assertEqual(instance_info.vnc_port, fake_vnc)
self.assertEqual("1.1.1.1", instance_info.ip)
self.assertEqual("fake_status", instance_info.status)
self.assertEqual("fake_type", instance_info.avd_type)
diff --git a/list/list.py b/list/list.py
index 6afaf0a..febd6f3 100644
--- a/list/list.py
+++ b/list/list.py
@@ -20,8 +20,7 @@
from __future__ import print_function
import getpass
import logging
-import re
-import subprocess
+import os
from acloud import errors
from acloud.internal import constants
@@ -35,28 +34,6 @@
logger = logging.getLogger(__name__)
_COMMAND_PS_LAUNCH_CVD = ["ps", "-wweo", "lstart,cmd"]
-_RE_LOCAL_CVD_PORT = re.compile(r"^127\.0\.0\.1:65(?P<cvd_port_suffix>\d{2})\s+")
-
-
-def GetActiveCVDIds():
- """Get active local cvd ids from adb devices.
-
- The adb port of local instance will be decided according to instance id.
- The rule of adb port will be '6520 + [instance id] - 1'. So we grep last
- two digits of port and calculate the instance id.
-
- Return:
- List of cvd id.
- """
- local_cvd_ids = []
- adb_cmd = [constants.ADB_BIN, "devices"]
- device_info = subprocess.check_output(adb_cmd)
- for device in device_info.splitlines():
- match = _RE_LOCAL_CVD_PORT.match(device)
- if match:
- cvd_serial = match.group("cvd_port_suffix")
- local_cvd_ids.append(int(cvd_serial) - 19)
- return local_cvd_ids
def _ProcessInstances(instance_list):
@@ -144,19 +121,39 @@
Returns:
instance_list: List of local instances.
"""
- local_cvd_ids = GetActiveCVDIds()
local_instance_list = []
- for cvd_id in local_cvd_ids:
- try:
- cf_runtime_cfg = instance.GetCuttlefishRuntimeConfig(cvd_id)
- local_instance_list.append(
- instance.LocalInstance(cvd_id, cf_runtime_cfg))
- except errors.ConfigError:
- logger.error("Instance[id:%d] dir not found!", cvd_id)
-
+ for cf_runtime_config_path in instance.GetAllLocalInstanceConfigs():
+ ins = instance.LocalInstance(cf_runtime_config_path)
+ if ins.CvdStatus():
+ local_instance_list.append(ins)
+ else:
+ logger.info("cvd runtime config found but instance is not active:%s"
+ , cf_runtime_config_path)
return local_instance_list
+def GetActiveCVD(local_instance_id):
+ """Check if the local AVD with specific instance id is running
+
+ Args:
+ local_instance_id: Integer of instance id.
+
+ Return:
+ LocalInstance object.
+ """
+ cfg_path = instance.GetLocalInstanceConfig(local_instance_id)
+ if cfg_path:
+ ins = instance.LocalInstance(cfg_path)
+ if ins.CvdStatus():
+ return ins
+ cfg_path = instance.GetDefaultCuttlefishConfig()
+ if local_instance_id == 1 and os.path.isfile(cfg_path):
+ ins = instance.LocalInstance(cfg_path)
+ if ins.CvdStatus():
+ return ins
+ return None
+
+
def GetLocalInstances():
"""Look for local cuttleifsh and goldfish instances.
@@ -308,7 +305,7 @@
"""
all_instance_info = []
for instance_object in instances:
- if instance_object.forwarding_adb_port == adb_port:
+ if instance_object.adb_port == adb_port:
return [instance_object]
all_instance_info.append(instance_object.fullname)
diff --git a/list/list_test.py b/list/list_test.py
index 12f7ce4..a4b466c 100644
--- a/list/list_test.py
+++ b/list/list_test.py
@@ -12,12 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for list."""
-import subprocess
+
import unittest
import mock
from acloud import errors
+from acloud.internal.lib import cvd_runtime_config
from acloud.internal.lib import driver_test_lib
from acloud.internal.lib import utils
from acloud.list import list as list_instance
@@ -91,7 +92,7 @@
def testFilterInstancesByAdbPort(self):
"""test FilterInstancesByAdbPort."""
alive_instance1 = InstanceObject("alive_instance1")
- alive_instance1.forwarding_adb_port = 1111
+ alive_instance1.adb_port = 1111
alive_instance1.fullname = "device serial: 127.0.0.1:1111 alive_instance1"
expected_instance = [alive_instance1]
# Test to find instance by adb port number.
@@ -105,17 +106,24 @@
# pylint: disable=protected-access
def testGetLocalCuttlefishInstances(self):
"""test _GetLocalCuttlefishInstances."""
- cf_config = mock.MagicMock()
-
# Test getting two instance case
- self.Patch(list_instance, "GetActiveCVDIds", return_value=[1, 2])
- self.Patch(instance, "GetCuttlefishRuntimeConfig", return_value=cf_config)
+ self.Patch(instance, "GetAllLocalInstanceConfigs",
+ return_value=["fake_path1", "fake_path2"])
self.Patch(instance, "GetLocalInstanceRuntimeDir")
- self.Patch(instance, "LocalInstance")
+
+ local_ins = mock.MagicMock()
+ local_ins.CvdStatus.return_value = True
+ self.Patch(instance, "LocalInstance", return_value=local_ins)
ins_list = list_instance._GetLocalCuttlefishInstances()
self.assertEqual(2, len(ins_list))
+ local_ins = mock.MagicMock()
+ local_ins.CvdStatus.return_value = False
+ self.Patch(instance, "LocalInstance", return_value=local_ins)
+ ins_list = list_instance._GetLocalCuttlefishInstances()
+ self.assertEqual(0, len(ins_list))
+
# pylint: disable=no-member
def testPrintInstancesDetails(self):
"""test PrintInstancesDetails."""
@@ -125,10 +133,13 @@
x_res=728,
y_res=728,
dpi=240,
- instance_dir="fake_dir"
+ instance_dir="fake_dir",
+ adb_ip_port="127.0.0.1:6520"
)
+ self.Patch(cvd_runtime_config, "CvdRuntimeConfig",
+ return_value=cf_config)
- ins = instance.LocalInstance(1, cf_config)
+ ins = instance.LocalInstance("fake_cf_path")
list_instance.PrintInstancesDetails([ins], verbose=True)
instance.Instance.Summary.assert_called_once()
@@ -141,15 +152,6 @@
list_instance.PrintInstancesDetails([], verbose=True)
instance.Instance.Summary.assert_not_called()
- # pylint: disable=no-member
- def testGetActiveCVDIds(self):
- """test GetActiveCVDIds."""
- # Test getting two local devices
- adb_output = "127.0.0.1:6520 device\n127.0.0.1:6521 device"
- expected_result = [1, 2]
- self.Patch(subprocess, "check_output", return_value=adb_output)
- self.assertEqual(list_instance.GetActiveCVDIds(), expected_result)
-
if __name__ == "__main__":
unittest.main()
diff --git a/public/report.py b/public/report.py
index 5f5a07a..dd95c4e 100755
--- a/public/report.py
+++ b/public/report.py
@@ -150,7 +150,7 @@
"requested to update to a status with lower severity %s, ignored.",
self.status, status)
- def AddDevice(self, instance_name, ip_address, adb_port, vnc_port=None,
+ def AddDevice(self, instance_name, ip_address, adb_port, vnc_port,
key="devices"):
"""Add a record of a device.
@@ -161,9 +161,13 @@
vnc_port: An integer.
key: A string, the data entry where the record is added.
"""
- device = {constants.INSTANCE_NAME: instance_name,
- constants.IP: "%s:%d" % (ip_address, adb_port),
- constants.ADB_PORT: adb_port}
+ device = {constants.INSTANCE_NAME: instance_name}
+ if adb_port:
+ device[constants.ADB_PORT] = adb_port
+ device[constants.IP] = "%s:%d" % (ip_address, adb_port)
+ else:
+ device[constants.IP] = ip_address
+
if vnc_port:
device[constants.VNC_PORT] = vnc_port
self.AddData(key=key, value=device)
diff --git a/public/report_test.py b/public/report_test.py
index 772c987..d3987c8 100644
--- a/public/report_test.py
+++ b/public/report_test.py
@@ -63,12 +63,13 @@
def testAddDevice(self):
"""test AddDevice."""
test_report = report.Report("create")
- test_report.AddDevice("instance_1", "127.0.0.1", 6520)
+ test_report.AddDevice("instance_1", "127.0.0.1", 6520, 6444)
expected = {
"devices": [{
"instance_name": "instance_1",
"ip": "127.0.0.1:6520",
- "adb_port": 6520
+ "adb_port": 6520,
+ "vnc_port": 6444
}]
}
self.assertEqual(test_report.data, expected)
diff --git a/reconnect/reconnect.py b/reconnect/reconnect.py
index 1be9567..d354b4e 100644
--- a/reconnect/reconnect.py
+++ b/reconnect/reconnect.py
@@ -22,7 +22,6 @@
import re
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
@@ -51,7 +50,7 @@
vnc_started_pattern = _VNC_STARTED_PATTERN % {"vnc_port": vnc_port}
if not utils.IsCommandRunning(vnc_started_pattern):
#clean old disconnect ssvnc viewer.
- delete.CleanupSSVncviewer(vnc_port)
+ utils.CleanupSSVncviewer(vnc_port)
match = _RE_DISPLAY.match(display)
if match:
@@ -110,9 +109,9 @@
"unknown avd type: %s" %
(instance.name, instance.avd_type))
- adb_cmd = AdbTools(instance.forwarding_adb_port)
- vnc_port = instance.forwarding_vnc_port
- adb_port = instance.forwarding_adb_port
+ adb_cmd = AdbTools(instance.adb_port)
+ vnc_port = instance.vnc_port
+ adb_port = instance.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()
diff --git a/reconnect/reconnect_test.py b/reconnect/reconnect_test.py
index 493067b..70ea801 100644
--- a/reconnect/reconnect_test.py
+++ b/reconnect/reconnect_test.py
@@ -42,7 +42,7 @@
instance_object = mock.MagicMock()
instance_object.ip = "1.1.1.1"
instance_object.islocal = False
- instance_object.forwarding_adb_port = "8686"
+ instance_object.adb_port = "8686"
instance_object.avd_type = "cuttlefish"
self.Patch(subprocess, "check_call", return_value=True)
self.Patch(utils, "LaunchVncClient")
@@ -52,7 +52,7 @@
self.Patch(utils, "IsCommandRunning", return_value=False)
#test ssh tunnel not connected, remote instance.
- instance_object.forwarding_vnc_port = 6666
+ instance_object.vnc_port = 6666
instance_object.display = ""
utils.AutoConnect.call_count = 0
reconnect.ReconnectInstance(ssh_private_key_path, instance_object, fake_report)
@@ -69,7 +69,7 @@
instance_object.ssh_tunnel_is_connected = False
instance_object.display = ""
utils.AutoConnect.call_count = 0
- instance_object.forwarding_vnc_port = 5555
+ instance_object.vnc_port = 5555
extra_args_ssh_tunnel = None
self.Patch(utils, "AutoConnect",
return_value=ForwardedPorts(vnc_port=11111, adb_port=22222))
@@ -100,7 +100,7 @@
#test reconnect local instance.
instance_object.islocal = True
instance_object.display = ""
- instance_object.forwarding_vnc_port = 5555
+ instance_object.vnc_port = 5555
instance_object.ssh_tunnel_is_connected = False
utils.AutoConnect.call_count = 0
reconnect.ReconnectInstance(ssh_private_key_path,
@@ -115,8 +115,8 @@
fake_report = mock.MagicMock()
instance_object = mock.MagicMock()
instance_object.ip = "1.1.1.1"
- instance_object.forwarding_vnc_port = 9999
- instance_object.forwarding_adb_port = "9999"
+ instance_object.vnc_port = 9999
+ instance_object.adb_port = "9999"
instance_object.islocal = False
instance_object.ssh_tunnel_is_connected = False
self.Patch(utils, "AutoConnect")
diff --git a/setup/host_setup_runner.py b/setup/host_setup_runner.py
index a334c2c..9a668e5 100644
--- a/setup/host_setup_runner.py
+++ b/setup/host_setup_runner.py
@@ -23,7 +23,10 @@
import getpass
import logging
+import os
+import shutil
import sys
+import tempfile
from acloud.internal import constants
from acloud.internal.lib import utils
@@ -33,17 +36,20 @@
logger = logging.getLogger(__name__)
-# Install cuttlefish-common will probably not work now.
-# TODO: update this to pull from the proper repo.
-_AVD_REQUIRED_PKGS = ["cuttlefish-common",
- # TODO(b/117613492): This is all qemu related, take this
- # out once they are added back in as deps for
- # cuttlefish-common.
- "qemu-kvm", "qemu-system-common", "qemu-system-x86",
- "qemu-utils", "libvirt-clients", "libvirt-daemon-system"]
+# Packages "devscripts" and "equivs" are required for "mk-build-deps".
+_AVD_REQUIRED_PKGS = [
+ "devscripts", "equivs", "libvirt-clients", "libvirt-daemon-system"]
_BASE_REQUIRED_PKGS = ["ssvnc", "lzop"]
+_CUTTLEFISH_COMMOM_PKG = "cuttlefish-common"
+_CF_COMMOM_FOLDER = "cf-common"
_LIST_OF_MODULES = ["kvm_intel", "kvm"]
_UPDATE_APT_GET_CMD = "sudo apt-get update"
+_INSTALL_CUTTLEFISH_COMMOM_CMD = [
+ "git clone https://github.com/google/android-cuttlefish.git {git_folder}",
+ "cd {git_folder}",
+ "yes | sudo mk-build-deps -i -r -B",
+ "dpkg-buildpackage -uc -us",
+ "sudo apt-get install -y -f ../cuttlefish-common_*_amd64.deb"]
class BasePkgInstaller(base_task_runner.BaseTaskRunner):
@@ -109,6 +115,47 @@
PACKAGES = _BASE_REQUIRED_PKGS
+class CuttlefishCommonPkgInstaller(base_task_runner.BaseTaskRunner):
+ """Subtask base runner class for installing cuttlefish-common."""
+
+ WELCOME_MESSAGE_TITLE = "Install cuttlefish-common packages on the host"
+ WELCOME_MESSAGE = ("This step will walk you through the cuttlefish-common "
+ "packages installation for your host.")
+
+ def ShouldRun(self):
+ """Check if cuttlefish-common package is installed.
+
+ Returns:
+ Boolean, True if cuttlefish-common is not installed.
+ """
+ if not utils.IsSupportedPlatform():
+ return False
+
+ # Any required package is not installed or not up-to-date will need to
+ # run installation task.
+ if not setup_common.PackageInstalled(_CUTTLEFISH_COMMOM_PKG):
+ return True
+ return False
+
+ def _Run(self):
+ """Install cuttlefilsh-common packages."""
+ cf_common_path = os.path.join(tempfile.mkdtemp(), _CF_COMMOM_FOLDER)
+ logger.debug("cuttlefish-common path: %s", cf_common_path)
+ cmd = "\n".join(sub_cmd.format(git_folder=cf_common_path)
+ for sub_cmd in _INSTALL_CUTTLEFISH_COMMOM_CMD)
+
+ if not utils.GetUserAnswerYes("\nStart to install cuttlefish-common :\n%s"
+ "\nPress 'y' to continue or anything "
+ "else to do it myself and run acloud "
+ "again[y/N]: " % cmd):
+ sys.exit(constants.EXIT_BY_USER)
+ try:
+ setup_common.CheckCmdOutput(cmd, shell=True)
+ finally:
+ shutil.rmtree(os.path.dirname(cf_common_path))
+ logger.info("Cuttlefish-common package installed now.")
+
+
class CuttlefishHostSetup(base_task_runner.BaseTaskRunner):
"""Subtask class that setup host for cuttlefish."""
diff --git a/setup/host_setup_runner_test.py b/setup/host_setup_runner_test.py
index b424db5..111540e 100644
--- a/setup/host_setup_runner_test.py
+++ b/setup/host_setup_runner_test.py
@@ -13,13 +13,17 @@
# limitations under the License.
"""Tests for host_setup_runner."""
import platform
+import shutil
+import tempfile
import unittest
+import mock
from acloud.internal.lib import driver_test_lib
from acloud.internal.lib import utils
from acloud.setup import setup_common
-from acloud.setup.host_setup_runner import CuttlefishHostSetup
from acloud.setup.host_setup_runner import AvdPkgInstaller
+from acloud.setup.host_setup_runner import CuttlefishCommonPkgInstaller
+from acloud.setup.host_setup_runner import CuttlefishHostSetup
class CuttlefishHostSetupTest(driver_test_lib.BaseDriverTest):
@@ -85,9 +89,36 @@
def testShouldNotRun(self):
"""Test ShoudRun should raise error in non-linux os."""
self.Patch(platform, "system", return_value="Mac")
-
self.assertFalse(self.AvdPkgInstaller.ShouldRun())
+class CuttlefishCommonPkgInstallerTest(driver_test_lib.BaseDriverTest):
+ """Test CuttlefishCommonPkgInstallerTest."""
+
+ # pylint: disable=invalid-name
+ def setUp(self):
+ """Set up the test."""
+ super(CuttlefishCommonPkgInstallerTest, self).setUp()
+ self.CuttlefishCommonPkgInstaller = CuttlefishCommonPkgInstaller()
+
+ def testShouldRun(self):
+ """Test ShoudRun."""
+ self.Patch(platform, "system", return_value="Linux")
+ self.Patch(setup_common, "PackageInstalled", return_value=False)
+ self.assertTrue(self.CuttlefishCommonPkgInstaller.ShouldRun())
+
+ @mock.patch.object(shutil, "rmtree")
+ @mock.patch.object(setup_common, "CheckCmdOutput")
+ def testRun(self, mock_cmd, mock_rmtree):
+ """Test Run."""
+ fake_tmp_folder = "/tmp/cf-common"
+ self.Patch(tempfile, "mkdtemp", return_value=fake_tmp_folder)
+ self.Patch(utils, "GetUserAnswerYes", return_value="y")
+ self.Patch(CuttlefishCommonPkgInstaller, "ShouldRun", return_value=True)
+ self.CuttlefishCommonPkgInstaller.Run()
+ self.assertEqual(mock_cmd.call_count, 1)
+ mock_rmtree.assert_called_once_with(fake_tmp_folder)
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/setup/setup.py b/setup/setup.py
index 36b6ffd..39513dc 100644
--- a/setup/setup.py
+++ b/setup/setup.py
@@ -48,6 +48,7 @@
# 2.Init all subtasks in queue and traverse them.
host_base_runner = host_setup_runner.HostBasePkgInstaller()
host_avd_runner = host_setup_runner.AvdPkgInstaller()
+ host_cf_common_runner = host_setup_runner.CuttlefishCommonPkgInstaller()
host_env_runner = host_setup_runner.CuttlefishHostSetup()
gcp_runner = gcp_setup_runner.GcpTaskRunner(args.config_file)
task_queue = []
@@ -55,6 +56,7 @@
if args.host:
task_queue.append(host_base_runner)
task_queue.append(host_avd_runner)
+ task_queue.append(host_cf_common_runner)
task_queue.append(host_env_runner)
# We should do these setup tasks if specified or if no args were used.
if args.host_base or (not args.host and not args.gcp_init):