aboutsummaryrefslogtreecommitdiffstats
path: root/resources/libraries/python/VppApiCrc.py
diff options
context:
space:
mode:
Diffstat (limited to 'resources/libraries/python/VppApiCrc.py')
-rw-r--r--resources/libraries/python/VppApiCrc.py99
1 files changed, 83 insertions, 16 deletions
diff --git a/resources/libraries/python/VppApiCrc.py b/resources/libraries/python/VppApiCrc.py
index 693dac064a..a8947a18cb 100644
--- a/resources/libraries/python/VppApiCrc.py
+++ b/resources/libraries/python/VppApiCrc.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2019 Cisco and/or its affiliates.
+# Copyright (c) 2023 Cisco and/or its affiliates.
# 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:
@@ -75,8 +75,9 @@ class VppApiCrcChecker:
Starts the same as _expected, but each time an encountered api,crc pair
fits the expectation, the pair is removed from all collections
- within this mapping. Ideally, the active mappings will become empty.
- If not, it is an error, VPP removed or renamed a message CSIT needs."""
+ within this mapping. It is fine if an api is missing
+ from some collections, as long as it is not missing from all collections
+ that remained in _expected."""
self._found = dict()
"""Mapping from API name to CRC string.
@@ -84,6 +85,12 @@ class VppApiCrcChecker:
This gets populated with CRCs found in .api.json,
to serve as a hint when reporting errors."""
+ self._options = dict()
+ """Mapping from API name to options dictionary.
+
+ This gets populated with options found in .api.json,
+ to serve as a hint when reporting errors."""
+
self._reported = dict()
"""Mapping from API name to CRC string.
@@ -171,7 +178,34 @@ class VppApiCrcChecker:
return _str(crc)
raise RuntimeError(f"No CRC found for message: {msg_obj!r}")
- def _process_crc(self, api_name, crc):
+ @staticmethod
+ def _get_options(msg_obj, version):
+ """Utility function to extract API options from an intermediate json.
+
+ Empty dict is returned if options are not found,
+ so old VPP builds can be tested without spamming.
+ If version starts with "0.", add a fake option,
+ as the message is treated as "in-progress" by the API upgrade process.
+
+ :param msg_obj: Loaded json object, item of "messages" list.
+ :param version: Version string from the .api.json file.
+ :type msg_obj: list of various types
+ :type version: Optional[str]
+ :returns: Object found as value for "options" key.
+ :rtype: dict
+ """
+ options = dict()
+ for item in reversed(msg_obj):
+ if not isinstance(item, dict):
+ continue
+ options = item.get(u"options", dict())
+ if not options:
+ break
+ if version is None or version.startswith(u"0."):
+ options[u"version"] = version
+ return options
+
+ def _process_crc(self, api_name, crc, options):
"""Compare API to verified collections, update class state.
Here, API stands for (message name, CRC) pair.
@@ -195,16 +229,21 @@ class VppApiCrcChecker:
Attempts to overwrite value in _found or _reported should not happen,
so the code does not check for that, simply overwriting.
+ Options are stored, to be examined later.
+
The intended usage is to call this method multiple times,
and then raise exception listing all _reported.
:param api_name: API name to check.
:param crc: Discovered CRC to check for the name.
+ :param options: Empty dict or options value for in .api.json
:type api_name: str
:type crc: str
+ :type options: dict
"""
# Regardless of the result, remember as found.
self._found[api_name] = crc
+ self._options[api_name] = options
old_expected = self._expected
new_expected = old_expected.copy()
for collection_name, name_to_crc_mapping in old_expected.items():
@@ -244,11 +283,13 @@ class VppApiCrcChecker:
continue
with open(f"{root}/{filename}", u"rt") as file_in:
json_obj = json.load(file_in)
+ version = json_obj[u"options"].get(u"version", None)
msgs = json_obj[u"messages"]
for msg_obj in msgs:
msg_name = self._get_name(msg_obj)
msg_crc = self._get_crc(msg_obj)
- self._process_crc(msg_name, msg_crc)
+ msg_options = self._get_options(msg_obj, version)
+ self._process_crc(msg_name, msg_crc, msg_options)
logger.debug(f"Surviving collections: {self._expected.keys()!r}")
def report_initial_conflicts(self, report_missing=False):
@@ -285,16 +326,21 @@ class VppApiCrcChecker:
if not report_missing:
return
missing = {name: mapp for name, mapp in self._missing.items() if mapp}
- if missing:
- missing_indented = json.dumps(
- missing, indent=1, sort_keys=True, separators=[u",", u":"])
- self.log_and_raise(
- f"API CRCs missing from .api.json:\n{missing_indented}"
- )
+ if set(missing.keys()) < set(self._expected.keys()):
+ # There is a collection where nothing is missing.
+ return
+ missing_indented = json.dumps(
+ missing, indent=1, sort_keys=True, separators=[u",", u":"]
+ )
+ self.log_and_raise(
+ f"API CRCs missing from .api.json:\n{missing_indented}"
+ )
def check_api_name(self, api_name):
"""Fail if the api_name has no, or different from known CRC associated.
+ Print warning if options contain anything more than vat_help.
+
Do not fail if this particular failure has been already reported.
Intended use: Call during test (not in initialization),
@@ -328,9 +374,30 @@ class VppApiCrcChecker:
if name_to_crc_mapping[api_name] == crc:
matching = True
break
- if matching:
+ if not matching:
+ self._reported[api_name] = crc
+ self.log_and_raise(
+ f"No active collection has API {api_name!r} with CRC {crc!r}"
+ )
+ options = self._options.get(api_name, None)
+ if not options:
+ # None means CSIT is attempting a new API on an old VPP build.
+ # If that is an issue, the API has been reported as missing already.
return
- self._reported[api_name] = crc
- self.log_and_raise(
- f"No active collection contains API {api_name!r} with CRC {crc!r}"
- )
+ options.pop(u"vat_help", None)
+ if options:
+ self._reported[api_name] = crc
+ logger.console(f"{api_name} used but has options {options}")
+
+ def print_warnings(self):
+ """Call check_api_name for API names in surviving collections.
+
+ Useful for VPP CRC checking job.
+ The API name is only checked when it appears
+ in all surviving collections.
+ """
+ api_name_to_crc_maps = self._expected.values()
+ api_name_sets = (set(n2c.keys()) for n2c in api_name_to_crc_maps)
+ api_names = set.intersection(*api_name_sets)
+ for api_name in sorted(api_names):
+ self.check_api_name(api_name)