New command entry for acloud pull function
- Add new command for '$acloud pull'.
- Add args "--instance-name" to specify which instance to pull.
Bug: 120613398
Bug: 112274919
Test: acloud-dev pull
acloud-dev pull --instance-name ins_name
Change-Id: I45e43a2a2a63dec3bae475fff57fde398e4cc915
diff --git a/Android.bp b/Android.bp
index 4d36801..3ff0421 100644
--- a/Android.bp
+++ b/Android.bp
@@ -46,6 +46,7 @@
"acloud_reconnect",
"acloud_internal",
"acloud_list",
+ "acloud_pull",
"acloud_metrics",
"acloud_proto",
"acloud_public",
@@ -79,9 +80,10 @@
libs: [
"acloud_create",
"acloud_delete",
- "acloud_reconnect",
+ "acloud_reconnect",
"acloud_internal",
"acloud_list",
+ "acloud_pull",
"acloud_proto",
"acloud_public",
"acloud_setup",
@@ -177,6 +179,14 @@
}
python_library_host{
+ name: "acloud_pull",
+ defaults: ["acloud_default"],
+ srcs: [
+ "pull/*.py",
+ ],
+}
+
+python_library_host{
name: "acloud_metrics",
defaults: ["acloud_default"],
srcs: [
diff --git a/errors.py b/errors.py
index 2b47cfa..79d0e90 100644
--- a/errors.py
+++ b/errors.py
@@ -220,7 +220,8 @@
class UnknownAvdType(Exception):
- """Unknow AVD type."""
+ """Unknown AVD type."""
+
class UnknownType(Exception):
- """Unknow type."""
+ """Unknown type."""
diff --git a/list/list.py b/list/list.py
index 9367efb..4034171 100644
--- a/list/list.py
+++ b/list/list.py
@@ -151,6 +151,34 @@
return instances_list
+def ChooseOneRemoteInstance(cfg):
+ """Get one remote cuttlefish instance.
+
+ Retrieve all remote cuttlefish instances and if there is more than 1 instance
+ found, ask user which instance they'd like.
+
+ Args:
+ cfg: AcloudConfig object.
+
+ Raises:
+ errors.NoInstancesFound: No cuttlefish remote instance found.
+
+ Returns:
+ list.Instance() object.
+ """
+ instances_list = GetCFRemoteInstances(cfg)
+ if not instances_list:
+ raise errors.NoInstancesFound(
+ "Can't find any cuttlefish remote instances, please try "
+ "'$acloud create' to create instances")
+ if len(instances_list) > 1:
+ print("Multiple instances detected, choose any one to proceed:")
+ instances = utils.GetAnswerFromList(instances_list,
+ enable_choose_all=False)
+ return instances[0]
+
+ return instances_list[0]
+
def GetInstancesFromInstanceNames(cfg, instance_names):
"""Get instances from instance names.
@@ -213,6 +241,19 @@
raise errors.NoInstancesFound(hint_message)
+def GetCFRemoteInstances(cfg):
+ """Look for cuttlefish remote instances.
+
+ Args:
+ cfg: AcloudConfig object.
+
+ Returns:
+ instance_list: List of instance names.
+ """
+ instances = GetRemoteInstances(cfg)
+ return [ins for ins in instances if ins.avd_type == constants.TYPE_CF]
+
+
def Run(args):
"""Run list.
diff --git a/list/list_test.py b/list/list_test.py
index d42fa37..dc328ee 100644
--- a/list/list_test.py
+++ b/list/list_test.py
@@ -18,6 +18,7 @@
from acloud import errors
from acloud.internal.lib import driver_test_lib
+from acloud.internal.lib import utils
from acloud.list import list as list_instance
@@ -61,6 +62,29 @@
cfg=cfg,
instance_names=instance_names)
+ def testChooseOneRemoteInstance(self):
+ """test choose one remote instance from instance names."""
+ cfg = mock.MagicMock()
+
+ # Test only one instance case
+ instance_names = ["cf_instance1"]
+ self.Patch(list_instance, "GetCFRemoteInstances", return_value=instance_names)
+ expected_instance = "cf_instance1"
+ self.assertEqual(list_instance.ChooseOneRemoteInstance(cfg), expected_instance)
+
+ # Test no instance case
+ self.Patch(list_instance, "GetCFRemoteInstances", return_value=[])
+ with self.assertRaises(errors.NoInstancesFound):
+ list_instance.ChooseOneRemoteInstance(cfg)
+
+ # Test two instances case.
+ instance_names = ["cf_instance1", "cf_instance2"]
+ choose_instance = ["cf_instance2"]
+ self.Patch(list_instance, "GetCFRemoteInstances", return_value=instance_names)
+ self.Patch(utils, "GetAnswerFromList", return_value=choose_instance)
+ expected_instance = "cf_instance2"
+ self.assertEqual(list_instance.ChooseOneRemoteInstance(cfg), expected_instance)
+
# pylint: disable=attribute-defined-outside-init
def testGetInstanceFromAdbPort(self):
"""test GetInstanceFromAdbPort."""
diff --git a/public/acloud_main.py b/public/acloud_main.py
index 28bdf30..fb69e5e 100644
--- a/public/acloud_main.py
+++ b/public/acloud_main.py
@@ -113,6 +113,8 @@
from acloud.public import device_driver
from acloud.public.actions import create_cuttlefish_action
from acloud.public.actions import create_goldfish_action
+from acloud.pull import pull
+from acloud.pull import pull_args
from acloud.setup import setup
from acloud.setup import setup_args
@@ -142,6 +144,7 @@
list_args.CMD_LIST,
delete_args.CMD_DELETE,
reconnect_args.CMD_RECONNECT,
+ pull_args.CMD_PULL,
])
parser = argparse.ArgumentParser(
description=__doc__,
@@ -231,9 +234,12 @@
# Command "list"
subparser_list.append(list_args.GetListArgParser(subparsers))
- # Command "Reconnect"
+ # Command "reconnect"
subparser_list.append(reconnect_args.GetReconnectArgParser(subparsers))
+ # Command "pull"
+ subparser_list.append(pull_args.GetPullArgParser(subparsers))
+
# Add common arguments.
for subparser in subparser_list:
acloud_common.AddCommonArguments(subparser)
@@ -404,6 +410,8 @@
list_instances.Run(args)
elif args.which == reconnect_args.CMD_RECONNECT:
reconnect.Run(args)
+ elif args.which == pull_args.CMD_PULL:
+ report = pull.Run(args)
elif args.which == setup_args.CMD_SETUP:
setup.Run(args)
else:
diff --git a/pull/__init__.py b/pull/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pull/__init__.py
diff --git a/pull/pull.py b/pull/pull.py
new file mode 100644
index 0000000..dc548ae
--- /dev/null
+++ b/pull/pull.py
@@ -0,0 +1,61 @@
+# 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"""Pull entry point.
+
+This command will pull the log files from a remote instance for AVD troubleshooting.
+"""
+
+from __future__ import print_function
+import logging
+
+from acloud.list import list as list_instances
+from acloud.public import config
+from acloud.public import report
+
+
+logger = logging.getLogger(__name__)
+
+
+def PullFileFromInstance(instance):
+ """Pull file from remote CF instance.
+
+ Args:
+ instance: list.Instance() object.
+
+ Returns:
+ A Report instance.
+ """
+ # TODO(120613398): rewrite this function to pull file from the remote instance.
+ print("We will pull file from the instance: %s." % instance.name)
+ return report.Report(command="pull")
+
+
+def Run(args):
+ """Run pull.
+
+ After pull command executed, tool will return one Report instance.
+ If there is no instance to pull, just return empty Report.
+
+ Args:
+ args: Namespace object from argparse.parse_args.
+
+ Returns:
+ A Report instance.
+ """
+ cfg = config.GetAcloudConfig(args)
+ if args.instance_name:
+ instance = list_instances.GetInstancesFromInstanceNames(
+ cfg, [args.instance_name])
+ return PullFileFromInstance(instance[0])
+ return PullFileFromInstance(list_instances.ChooseOneRemoteInstance(cfg))
diff --git a/pull/pull_args.py b/pull/pull_args.py
new file mode 100644
index 0000000..1772eb4
--- /dev/null
+++ b/pull/pull_args.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+#
+# 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"""Pull args.
+
+Defines the pull arg parser that holds pull specific args.
+"""
+import argparse
+
+
+CMD_PULL = "pull"
+
+
+def GetPullArgParser(subparser):
+ """Return the pull arg parser.
+
+ Args:
+ subparser: argparse.ArgumentParser that is attached to main acloud cmd.
+
+ Returns:
+ argparse.ArgumentParser with pull options defined.
+ """
+ pull_parser = subparser.add_parser(CMD_PULL)
+ pull_parser.required = False
+ pull_parser.set_defaults(which=CMD_PULL)
+ pull_group = pull_parser.add_mutually_exclusive_group()
+ pull_group.add_argument(
+ "--instance-name",
+ dest="instance_name",
+ type=str,
+ required=False,
+ help="The name of the remote instance that need to pull log files.")
+
+ # TODO(b/118439885): Old arg formats to support transition, delete when
+ # transistion is done.
+ pull_group.add_argument(
+ "--instance_name",
+ dest="instance_name",
+ type=str,
+ required=False,
+ help=argparse.SUPPRESS)
+
+ return pull_parser