gsi_util: adding check_compat subcommand
'check_compat' command can check the compatibility between
system and vendor image, which can be any source supported by
mounters, ex. image file, folder or adb.
Uses following command for the detail:
$ ./gsu_util.py check_compat --help
The patch also includes a 'checker' framework. There is only
one checker 'VintfChecker' at this time. VintfChecker uses a
host tool, 'checkvintf', to check the compatibility.
Bug: 70253825
Test: check_compat with different mounters
Change-Id: I459b4cbd38465c0058087b4c68bca66e491c940e
diff --git a/gsi/gsi_util/Android.bp b/gsi/gsi_util/Android.bp
index cf72dd9..c1b2828 100644
--- a/gsi/gsi_util/Android.bp
+++ b/gsi/gsi_util/Android.bp
@@ -17,6 +17,7 @@
srcs: [
"gsi_util.py",
"gsi_util/*.py",
+ "gsi_util/checkers/*.py",
"gsi_util/commands/*.py",
"gsi_util/dumpers/*.py",
"gsi_util/mounters/*.py",
@@ -25,6 +26,7 @@
required: [
"adb",
"avbtool",
+ "checkvintf",
"simg2img",
],
version: {
diff --git a/gsi/gsi_util/gsi_util.py b/gsi/gsi_util/gsi_util.py
index 6dc0931..81c8ffc 100755
--- a/gsi/gsi_util/gsi_util.py
+++ b/gsi/gsi_util/gsi_util.py
@@ -28,7 +28,7 @@
# Adds gsi_util COMMAND here.
# TODO(bowgotsai): auto collect from gsi_util/commands/*.py
- _COMMANDS = ['flash_gsi', 'pull', 'dump', 'hello']
+ _COMMANDS = ['flash_gsi', 'pull', 'dump', 'check_compat', 'hello']
_LOGGING_FORMAT = '%(message)s'
_LOGGING_LEVEL = logging.WARNING
diff --git a/gsi/gsi_util/gsi_util/checkers/__init__.py b/gsi/gsi_util/gsi_util/checkers/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gsi/gsi_util/gsi_util/checkers/__init__.py
diff --git a/gsi/gsi_util/gsi_util/checkers/check_result.py b/gsi/gsi_util/gsi_util/checkers/check_result.py
new file mode 100644
index 0000000..d3dbede
--- /dev/null
+++ b/gsi/gsi_util/gsi_util/checkers/check_result.py
@@ -0,0 +1,19 @@
+# Copyright 2017 - 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 namedtuple CheckResultItem."""
+
+from collections import namedtuple
+
+CheckResultItem = namedtuple('CheckResultItem', 'name result message')
diff --git a/gsi/gsi_util/gsi_util/checkers/checker.py b/gsi/gsi_util/gsi_util/checkers/checker.py
new file mode 100644
index 0000000..cb79cb1
--- /dev/null
+++ b/gsi/gsi_util/gsi_util/checkers/checker.py
@@ -0,0 +1,57 @@
+# Copyright 2017 - 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 class Checker and maintain the checking list."""
+
+from collections import namedtuple
+
+from gsi_util.checkers.vintf_checker import VintfChecker
+
+CheckListItem = namedtuple('CheckListItem', 'id checker_class')
+
+_CHECK_LIST = [
+ CheckListItem('checkvintf', VintfChecker),
+]
+
+
+class Checker(object):
+ """Implement methods and utils to checking compatibility a FileAccessor."""
+
+ def __init__(self, file_accessor):
+ self._file_accessor = file_accessor
+
+ def check(self, check_list):
+ check_result_items = []
+
+ for x in check_list:
+ checker = x.checker_class(self._file_accessor)
+ check_result_items += checker.check()
+
+ return check_result_items
+
+ @staticmethod
+ def make_check_list_with_ids(ids):
+ check_list = []
+ for check_id in ids:
+ # Find the first item matched check_id
+ matched_check_item = next((x for x in _CHECK_LIST if x.id == check_id),
+ None)
+ if not matched_check_item:
+ raise RuntimeError('Unknown check ID: "{}"'.format(check_id))
+ check_list.append(matched_check_item)
+ return check_list
+
+ @staticmethod
+ def get_all_check_list():
+ return _CHECK_LIST
diff --git a/gsi/gsi_util/gsi_util/checkers/vintf_checker.py b/gsi/gsi_util/gsi_util/checkers/vintf_checker.py
new file mode 100644
index 0000000..1150e64
--- /dev/null
+++ b/gsi/gsi_util/gsi_util/checkers/vintf_checker.py
@@ -0,0 +1,42 @@
+# Copyright 2017 - 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.
+
+"""Provides class VintfChecker."""
+
+from gsi_util.checkers.check_result import CheckResultItem
+import gsi_util.utils.vintf_utils as vintf_utils
+
+
+class VintfChecker(object):
+
+ _SYSTEM_MANIFEST_XML = '/system/manifest.xml'
+ _VENDOR_MATRIX_XML = '/vendor/compatibility_matrix.xml'
+ _REQUIRED_FILES = [_SYSTEM_MANIFEST_XML, _VENDOR_MATRIX_XML]
+
+ def __init__(self, file_accessor):
+ self._file_accessor = file_accessor
+
+ def check(self):
+ fa = self._file_accessor
+
+ with fa.prepare_multi_files(self._REQUIRED_FILES) as [manifest, matrix]:
+ if not manifest:
+ raise RuntimeError('Cannot open manifest file: {}'.format(
+ self._SYSTEM_MANIFEST_XML))
+ if not matrix:
+ raise RuntimeError('Cannot open matrix file: {}'.format(
+ self._VENDOR_MATRIX_XML))
+
+ result, error_message = vintf_utils.checkvintf(manifest, matrix)
+ return [CheckResultItem('checkvintf', result, error_message)]
diff --git a/gsi/gsi_util/gsi_util/commands/check_compat.py b/gsi/gsi_util/gsi_util/commands/check_compat.py
new file mode 100644
index 0000000..d05533f
--- /dev/null
+++ b/gsi/gsi_util/gsi_util/commands/check_compat.py
@@ -0,0 +1,145 @@
+# Copyright 2017 - 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 command 'check_compat'."""
+
+import argparse
+import logging
+
+from gsi_util.checkers.checker import Checker
+from gsi_util.mounters.composite_mounter import CompositeMounter
+
+
+class CheckReporter(object):
+ """Output the checker result with formating."""
+
+ _OUTPUT_FORMAT = '{:30}: {}'
+ _ERR_MSE_FORMAT = ' {}'
+ _SUMMARY_NAME = 'summary'
+
+ @staticmethod
+ def _get_pass_str(is_pass):
+ return 'pass' if is_pass else 'fail'
+
+ def _output_result_item(self, result_item):
+ name, result, message = result_item
+ if not self._only_summary:
+ result_str = self._get_pass_str(result)
+ print self._OUTPUT_FORMAT.format(name, result_str)
+ if message:
+ print self._ERR_MSE_FORMAT.format(message)
+ return result
+
+ def _output_summary(self, summary_result):
+ summary_result_str = self._get_pass_str(summary_result)
+ print self._OUTPUT_FORMAT.format(self._SUMMARY_NAME, summary_result_str)
+
+ def __init__(self):
+ self._only_summary = False
+
+ def set_only_summary(self):
+ self._only_summary = True
+
+ def output(self, check_results):
+ all_pass = True
+ for result_item in check_results:
+ item_pass = self._output_result_item(result_item)
+ all_pass = all_pass and item_pass
+ self._output_summary(all_pass)
+
+
+def do_list_check(_):
+ for info in Checker.get_all_check_list():
+ print info.id
+
+
+def do_check_compat(args):
+ 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()
+
+ with mounter as file_accessor:
+ checker = Checker(file_accessor)
+ check_result = checker.check(check_list)
+
+ reporter = CheckReporter()
+ if args.only_summary:
+ reporter.set_only_summary()
+ reporter.output(check_result)
+
+ logging.info('==== DONE ====')
+
+
+DUMP_DESCRIPTION = """'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 could use command 'list_check' to query all IDs:
+
+ $ ./gsi_util.py list_check
+
+Here is an examples to check a system.img and a device are compatible:
+
+ $ ./gsi_util.py check_compat --system system.img --vendor adb"""
+
+
+def setup_command_args(parser):
+ """Setup command 'list_check' and 'check_compat'."""
+
+ # command 'list_check'
+ list_check_parser = parser.add_parser(
+ 'list_check', help='list all possible checking IDs')
+ list_check_parser.set_defaults(func=do_list_check)
+
+ # command 'check_compat'
+ check_compat_parser = parser.add_parser(
+ 'check_compat',
+ help='checks compatibility between a system and a vendor',
+ description=DUMP_DESCRIPTION,
+ 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(
+ '--only-summary',
+ action='store_true',
+ help='only output the summary result')
+ check_compat_parser.add_argument(
+ 'ID',
+ type=str,
+ nargs='*',
+ help='the checking ID to be dumped. Check all if not given')
+ check_compat_parser.set_defaults(func=do_check_compat)
diff --git a/gsi/gsi_util/gsi_util/utils/vintf_utils.py b/gsi/gsi_util/gsi_util/utils/vintf_utils.py
new file mode 100644
index 0000000..d51807e
--- /dev/null
+++ b/gsi/gsi_util/gsi_util/utils/vintf_utils.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+#
+# Copyright 2017 - 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.
+"""VINTF-related utilities."""
+
+import logging
+
+from gsi_util.utils.cmd_utils import run_command
+
+
+def checkvintf(manifest, matrix):
+ """call checkvintf.
+
+ Args:
+ manifest: manifest file
+ matrix: matrix file
+
+ Returns:
+ A tuple with (check_result, error_message)
+ """
+ logging.debug('checkvintf %s %s...', manifest, matrix)
+
+ # 'read_stdout=True' to disable output
+ (returncode, _, stderrdata) = run_command(
+ ['checkvintf', manifest, matrix],
+ raise_on_error=False,
+ read_stdout=True,
+ read_stderr=True)
+ return (returncode == 0, stderrdata)