Revise code of gsi_util help

The patch unifies the code of arguments '--vendor' and '--system',
and to be an argument group 'image sources'.

Bug: 64817940
Test: gsi_util.py for each commands and check the output
Change-Id: I591ef4b922f931b7ceaacc362979156ef830a54e
diff --git a/gsi/gsi_util/Android.bp b/gsi/gsi_util/Android.bp
index c1b2828..d1b1131 100644
--- a/gsi/gsi_util/Android.bp
+++ b/gsi/gsi_util/Android.bp
@@ -19,6 +19,7 @@
     "gsi_util/*.py",
     "gsi_util/checkers/*.py",
     "gsi_util/commands/*.py",
+    "gsi_util/commands/common/*.py",
     "gsi_util/dumpers/*.py",
     "gsi_util/mounters/*.py",
     "gsi_util/utils/*.py",
diff --git a/gsi/gsi_util/gsi_util/commands/check_compat.py b/gsi/gsi_util/gsi_util/commands/check_compat.py
index d05533f..21a5983 100644
--- a/gsi/gsi_util/gsi_util/commands/check_compat.py
+++ b/gsi/gsi_util/gsi_util/commands/check_compat.py
@@ -17,7 +17,7 @@
 import logging
 
 from gsi_util.checkers.checker import Checker
-from gsi_util.mounters.composite_mounter import CompositeMounter
+from gsi_util.commands.common import image_sources
 
 
 class CheckReporter(object):
@@ -67,15 +67,11 @@
   logging.info('==== CHECK_COMPAT ====')
   logging.info('  system=%s vendor=%s', args.system, args.vendor)
 
-  # args.system and args.vendor are required
-  mounter = CompositeMounter()
-  mounter.add_by_mount_target('system', args.system)
-  mounter.add_by_mount_target('vendor', args.vendor)
-
   logging.debug('Checking ID list: %s', args.ID)
   check_list = Checker.make_check_list_with_ids(args.ID) if len(
       args.ID) else Checker.get_all_check_list()
 
+  mounter = image_sources.create_composite_mounter_by_args(args)
   with mounter as file_accessor:
     checker = Checker(file_accessor)
     check_result = checker.check(check_list)
@@ -88,17 +84,9 @@
   logging.info('==== DONE ====')
 
 
-DUMP_DESCRIPTION = """'check_compat' command checks compatibility images
+_CHECK_COMPAT_DESC = """'check_compat' command checks compatibility images
 
-You must assign at least one image source by SYSTEM and/or VENDOR.
-Image source could be:
-
- adb[:SERIAL_NUM]: form the device which be connected with adb
-  image file name: from the given image file, e.g. the file name of a GSI.
-                   If a image file is assigned to be the source of system
-                   image, gsu_util will detect system-as-root automatically.
-      folder name: from the given folder, e.g. the system/vendor folder in an
-                   Android build out folder.
+You must assign both image sources by SYSTEM and VENDOR.
 
 You could use command 'list_check' to query all IDs:
 
@@ -121,22 +109,17 @@
   check_compat_parser = parser.add_parser(
       'check_compat',
       help='checks compatibility between a system and a vendor',
-      description=DUMP_DESCRIPTION,
+      description=_CHECK_COMPAT_DESC,
       formatter_class=argparse.RawTextHelpFormatter)
   check_compat_parser.add_argument(
-      '--system',
-      type=str,
-      required=True,
-      help='system image file name, folder name or "adb"')
-  check_compat_parser.add_argument(
-      '--vendor',
-      type=str,
-      required=True,
-      help='vendor image file name, folder name or "adb"')
-  check_compat_parser.add_argument(
+      '-s',
       '--only-summary',
       action='store_true',
       help='only output the summary result')
+  image_sources.add_argument_group(
+      check_compat_parser,
+      required_system=True,
+      required_vendor=True)
   check_compat_parser.add_argument(
       'ID',
       type=str,
diff --git a/gsi/gsi_util/gsi_util/commands/common/__init__.py b/gsi/gsi_util/gsi_util/commands/common/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gsi/gsi_util/gsi_util/commands/common/__init__.py
diff --git a/gsi/gsi_util/gsi_util/commands/common/image_sources.py b/gsi/gsi_util/gsi_util/commands/common/image_sources.py
new file mode 100644
index 0000000..9c6eb40
--- /dev/null
+++ b/gsi/gsi_util/gsi_util/commands/common/image_sources.py
@@ -0,0 +1,53 @@
+# Copyright 2018 - 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.
+"""Provide common implementation of image sources."""
+
+from gsi_util.mounters import composite_mounter
+
+_DESCRIPTION = """The image sources to be mount targets.
+
+An image source could be:
+
+ adb[:SERIAL_NUM]: form the device which be connected with adb
+  image file name: from the given image file, e.g. the file name of a GSI.
+                   If a image file is assigned to be the source of system
+                   image, gsi_util will detect system-as-root automatically.
+      folder name: from the given folder, e.g. the system/vendor folder in an
+                   Android build out folder.
+"""
+
+
+def create_composite_mounter_by_args(args):
+  mounter = composite_mounter.CompositeMounter()
+  if args.system:
+    mounter.add_by_mount_target('system', args.system)
+  if args.vendor:
+    mounter.add_by_mount_target('vendor', args.vendor)
+  return mounter
+
+
+def add_argument_group(parser, required_system=False, required_vendor=False):
+  """Add a argument group into the given parser for image sources."""
+
+  group = parser.add_argument_group('image sources', _DESCRIPTION)
+  group.add_argument(
+      '--system',
+      type=str,
+      required=required_system,
+      help='system image file name, folder name or "adb"')
+  group.add_argument(
+      '--vendor',
+      type=str,
+      required=required_vendor,
+      help='vendor image file name, folder name or "adb"')
diff --git a/gsi/gsi_util/gsi_util/commands/dump.py b/gsi/gsi_util/gsi_util/commands/dump.py
index f89db3e..b0ccaf4 100644
--- a/gsi/gsi_util/gsi_util/commands/dump.py
+++ b/gsi/gsi_util/gsi_util/commands/dump.py
@@ -11,15 +11,14 @@
 # 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.
-
 """Implementation of gsi_util command 'dump'."""
 
 import argparse
 import logging
 import sys
 
-from gsi_util.dumpers.dumper import Dumper
-from gsi_util.mounters.composite_mounter import CompositeMounter
+from gsi_util.commands.common import image_sources
+from gsi_util.dumpers import dumper
 
 
 class DumpReporter(object):
@@ -75,7 +74,7 @@
 
 
 def do_list_dump(_):
-  for info in Dumper.get_all_dump_list():
+  for info in dumper.Dumper.get_all_dump_list():
     print info.info_name
 
 
@@ -85,19 +84,14 @@
   if not args.system and not args.vendor:
     sys.exit('Without system nor vendor.')
 
-  mounter = CompositeMounter()
-  if args.system:
-    mounter.add_by_mount_target('system', args.system)
-  if args.vendor:
-    mounter.add_by_mount_target('vendor', args.vendor)
-
   logging.debug('Info name list: %s', args.INFO_NAME)
-  dump_list = Dumper.make_dump_list_by_name_list(args.INFO_NAME) if len(
-      args.INFO_NAME) else Dumper.get_all_dump_list()
+  dump_list = dumper.Dumper.make_dump_list_by_name_list(args.INFO_NAME) if len(
+      args.INFO_NAME) else dumper.Dumper.get_all_dump_list()
 
+  mounter = image_sources.create_composite_mounter_by_args(args)
   with mounter as file_accessor:
-    dumper = Dumper(file_accessor)
-    dump_result_dict = dumper.dump(dump_list)
+    d = dumper.Dumper(file_accessor)
+    dump_result_dict = d.dump(dump_list)
 
   # reserved for output to a file
   os = sys.stdout
@@ -109,17 +103,9 @@
   logging.info('==== DONE ====')
 
 
-DUMP_DESCRIPTION = """'dump' command dumps information from given image
+_DUMP_DESCRIPTION = ("""'dump' command dumps information from given image
 
 You must assign at least one image source by SYSTEM and/or VENDOR.
-Image source could be:
-
- adb[:SERIAL_NUM]: form the device which be connected with adb
-  image file name: from the given image file, e.g. the file name of a GSI.
-                   If a image file is assigned to be the source of system
-                   image, gsu_util will detect system-as-root automatically.
-      folder name: from the given folder, e.g. the system/vendor folder in an
-                   Android build out folder.
 
 You could use command 'list_dump' to query all info names:
 
@@ -136,7 +122,7 @@
 
     $ ./gsi_util.py dump --system adb --vendor adb
     $ ./gsi_util.py dump --system system.img --show-unknown
-    $ ./gsi_util.py dump --system my/out/folder/system"""
+    $ ./gsi_util.py dump --system my/out/folder/system""")
 
 
 def setup_command_args(parser):
@@ -149,17 +135,14 @@
   dump_parser = parser.add_parser(
       'dump',
       help='dump information from given image',
-      description=DUMP_DESCRIPTION,
+      description=_DUMP_DESCRIPTION,
       formatter_class=argparse.RawTextHelpFormatter)
   dump_parser.add_argument(
-      '--system', type=str, help='system image file name, folder name or "adb"')
-  dump_parser.add_argument(
-      '--vendor', type=str, help='vendor image file name, folder name or "adb"')
-  dump_parser.add_argument(
       '-u',
       '--show-unknown',
       action='store_true',
       help='force display the dump info items in list which does not exist')
+  image_sources.add_argument_group(dump_parser)
   dump_parser.add_argument(
       'INFO_NAME',
       type=str,
diff --git a/gsi/gsi_util/gsi_util/commands/pull.py b/gsi/gsi_util/gsi_util/commands/pull.py
index 0b03011..c9e6783 100644
--- a/gsi/gsi_util/gsi_util/commands/pull.py
+++ b/gsi/gsi_util/gsi_util/commands/pull.py
@@ -18,7 +18,7 @@
 import shutil
 import sys
 
-from gsi_util.mounters.composite_mounter import CompositeMounter
+from gsi_util.commands.common import image_sources
 
 
 def do_pull(args):
@@ -30,12 +30,7 @@
 
   source, dest = args.SOURCE, args.DEST
 
-  mounter = CompositeMounter()
-  if args.system:
-    mounter.add_by_mount_target('system', args.system)
-  if args.vendor:
-    mounter.add_by_mount_target('vendor', args.vendor)
-
+  mounter = image_sources.create_composite_mounter_by_args(args)
   with mounter as file_accessor:
     with file_accessor.prepare_file(source) as filename:
       if not filename:
@@ -47,18 +42,9 @@
   logging.info('==== DONE ====')
 
 
-DUMP_DESCRIPTION = """'pull' command pulls a file from the give image.
+_PULL_DESCRIPTION = ("""'pull' command pulls a file from the give image.
 
 You must assign at least one image source by SYSTEM and/or VENDOR.
-Image source could be:
-
- adb[:SERIAL_NUM]: pull the file form the device which be connected with adb
-  image file name: pull the file from the given image file, e.g. the file name
-                   of a GSI.
-                   If a image file is assigned to be the source of system
-                   image, gsu_util will detect system-as-root automatically.
-      folder name: pull the file from the given folder, e.g. the system/vendor
-                   folder in a Android build out folder.
 
 SOURCE is the full path file name to pull, which must start with '/' and
 includes the mount point. ex.
@@ -71,28 +57,25 @@
     $ ./gsi_util.py pull --system adb:AB0123456789 /system/manifest.xml
     $ ./gsi_util.py pull --vendor adb /vendor/compatibility_matrix.xml
     $ ./gsi_util.py pull --system system.img /system/build.prop
-    $ ./gsi_util.py pull --system my/out/folder/system /system/build.prop"""
+    $ ./gsi_util.py pull --system my/out/folder/system /system/build.prop""")
 
 
 def setup_command_args(parser):
   # command 'pull'
-  dump_parser = parser.add_parser(
+  pull_parser = parser.add_parser(
       'pull',
       help='pull a file from the given image',
-      description=DUMP_DESCRIPTION,
+      description=_PULL_DESCRIPTION,
       formatter_class=argparse.RawTextHelpFormatter)
-  dump_parser.add_argument(
-      '--system', type=str, help='system image file name, folder name or "adb"')
-  dump_parser.add_argument(
-      '--vendor', type=str, help='vendor image file name, folder name or "adb"')
-  dump_parser.add_argument(
+  image_sources.add_argument_group(pull_parser)
+  pull_parser.add_argument(
       'SOURCE',
       type=str,
       help='the full path file name in given image to be pull')
-  dump_parser.add_argument(
+  pull_parser.add_argument(
       'DEST',
       nargs='?',
       default='.',
       type=str,
       help='the file name or directory to save the pulled file (default: .)')
-  dump_parser.set_defaults(func=do_pull)
+  pull_parser.set_defaults(func=do_pull)
diff --git a/gsi/gsi_util/gsi_util/mounters/image_mounter.py b/gsi/gsi_util/gsi_util/mounters/image_mounter.py
index 22a32e5..6553eb3 100644
--- a/gsi/gsi_util/gsi_util/mounters/image_mounter.py
+++ b/gsi/gsi_util/gsi_util/mounters/image_mounter.py
@@ -18,6 +18,7 @@
 It can get files from an image file. e.g., system.img or vendor.img.
 """
 
+import errno
 import logging
 import os
 import shutil