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
This commit is contained in:
SzuWei Lin
2017-12-27 18:03:20 +08:00
parent 18d5e919fe
commit a30d9ef26b
8 changed files with 307 additions and 1 deletions

View File

@@ -17,6 +17,7 @@ python_binary_host {
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 @@ python_binary_host {
required: [
"adb",
"avbtool",
"checkvintf",
"simg2img",
],
version: {

View File

@@ -28,7 +28,7 @@ class GsiUtil(object):
# 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

View File

@@ -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')

View File

@@ -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

View File

@@ -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)]

View File

@@ -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)

View File

@@ -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)