Merge "Support mounting the odm image"
diff --git a/gsi/gsi_util/gsi_util/commands/check_compat.py b/gsi/gsi_util/gsi_util/commands/check_compat.py
index 1a04012..d2e107a 100644
--- a/gsi/gsi_util/gsi_util/commands/check_compat.py
+++ b/gsi/gsi_util/gsi_util/commands/check_compat.py
@@ -163,8 +163,7 @@
       help='only output the summary result')
   image_sources.add_argument_group(
       check_compat_parser,
-      required_system=True,
-      required_vendor=True)
+      required_images=['system', 'vendor'])
   check_compat_parser.add_argument(
       'CHECK_ITEM',
       type=str,
diff --git a/gsi/gsi_util/gsi_util/commands/common/image_sources.py b/gsi/gsi_util/gsi_util/commands/common/image_sources.py
index 9c6eb40..3251319 100644
--- a/gsi/gsi_util/gsi_util/commands/common/image_sources.py
+++ b/gsi/gsi_util/gsi_util/commands/common/image_sources.py
@@ -13,9 +13,11 @@
 # limitations under the License.
 """Provide common implementation of image sources."""
 
+import logging
+
 from gsi_util.mounters import composite_mounter
 
-_DESCRIPTION = """The image sources to be mount targets.
+_DESCRIPTION = """The image sources to be mounted targets.
 
 An image source could be:
 
@@ -29,25 +31,37 @@
 
 
 def create_composite_mounter_by_args(args):
+  """Creates a CompositeMounter by the images in given args."""
+
+  logging.info('Mount images...')
   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)
+  for partition in composite_mounter.SUPPORTED_PARTITIONS:
+    image_source = vars(args)[partition]
+    if image_source:
+      logging.info('  %s=%s', partition, image_source)
+      mounter.add_by_mount_target(partition, image_source)
+
+  if mounter.is_empty():
+    raise RuntimeError('Must give at least one image source.')
+
   return mounter
 
 
-def add_argument_group(parser, required_system=False, required_vendor=False):
-  """Add a argument group into the given parser for image sources."""
+def add_argument_group(parser, required_images=None):
+  """Add a argument group into the given parser for image sources.
+
+  Args:
+    parser: The parser to be added the argument group.
+    required_images: A list contains the required images. e.g.
+      ['system', 'vendor']. Default is no required images.
+  """
+  # To avoid pylint W0102
+  required_images = required_images or []
 
   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"')
+  for partition in composite_mounter.SUPPORTED_PARTITIONS:
+    group.add_argument(
+        '--' + partition,
+        type=str,
+        required=partition in required_images,
+        help='{} image file name, folder name or "adb"'.format(partition))
diff --git a/gsi/gsi_util/gsi_util/commands/dump.py b/gsi/gsi_util/gsi_util/commands/dump.py
index b0ccaf4..e772891 100644
--- a/gsi/gsi_util/gsi_util/commands/dump.py
+++ b/gsi/gsi_util/gsi_util/commands/dump.py
@@ -80,9 +80,6 @@
 
 def do_dump(args):
   logging.info('==== DUMP ====')
-  logging.info('  system=%s vendor=%s', args.system, args.vendor)
-  if not args.system and not args.vendor:
-    sys.exit('Without system nor vendor.')
 
   logging.debug('Info name list: %s', args.INFO_NAME)
   dump_list = dumper.Dumper.make_dump_list_by_name_list(args.INFO_NAME) if len(
@@ -105,7 +102,7 @@
 
 _DUMP_DESCRIPTION = ("""'dump' command dumps information from given image
 
-You must assign at least one image source by SYSTEM and/or VENDOR.
+You must assign at least one image source.
 
 You could use command 'list_dump' to query all info names:
 
diff --git a/gsi/gsi_util/gsi_util/commands/pull.py b/gsi/gsi_util/gsi_util/commands/pull.py
index c9e6783..c379173 100644
--- a/gsi/gsi_util/gsi_util/commands/pull.py
+++ b/gsi/gsi_util/gsi_util/commands/pull.py
@@ -23,10 +23,6 @@
 
 def do_pull(args):
   logging.info('==== PULL ====')
-  logging.info('  system=%s vendor=%s', args.system, args.vendor)
-
-  if not args.system and not args.vendor:
-    sys.exit('Without system nor vendor.')
 
   source, dest = args.SOURCE, args.DEST
 
@@ -44,7 +40,7 @@
 
 _PULL_DESCRIPTION = ("""'pull' command pulls a file from the give image.
 
-You must assign at least one image source by SYSTEM and/or VENDOR.
+You must assign at least one image source.
 
 SOURCE is the full path file name to pull, which must start with '/' and
 includes the mount point. ex.
diff --git a/gsi/gsi_util/gsi_util/mounters/composite_mounter.py b/gsi/gsi_util/gsi_util/mounters/composite_mounter.py
index 68f14b1..79f6490 100644
--- a/gsi/gsi_util/gsi_util/mounters/composite_mounter.py
+++ b/gsi/gsi_util/gsi_util/mounters/composite_mounter.py
@@ -34,11 +34,11 @@
 from gsi_util.mounters import folder_mounter
 from gsi_util.mounters import image_mounter
 
+SUPPORTED_PARTITIONS = ['system', 'vendor', 'odm']
+
 
 class _MounterFactory(object):
 
-  _SUPPORTED_PARTITIONS = ['system', 'vendor']
-
   @classmethod
   def create_by_mount_target(cls, mount_target, partition):
     """Create a proper Mounter instance by a string of mount target.
@@ -56,7 +56,7 @@
     Raises:
       ValueError: partiton is not support or mount_target is not exist.
     """
-    if partition not in cls._SUPPORTED_PARTITIONS:
+    if partition not in SUPPORTED_PARTITIONS:
       raise ValueError('Wrong partition name "{}"'.format(partition))
 
     if mount_target == 'adb' or mount_target.startswith('adb:'):
@@ -103,6 +103,9 @@
     super(CompositeMounter, self).__init__()
     self._mounters = []
 
+  def is_empty(self):
+    return not self._mounters
+
   # override
   def _handle_mount(self):
     file_accessors = [(path_prefix, mounter.mount())