A throw away script to validate that the generated XML has the correct versions. It checks if $keyword is present in $git_tag and also in the $next_git_tag but not in the $previous_git_tag. Takes almost a minute to run, in my computer. | python3 ./scripts/version-quest.py -i ./include/ -d ./build/docs/ | # libvirt has 2101 symbols | # libvirt-qemu has 19 symbols | # libvirt-lxc has 4 symbols | # libvirt-admin has 63 symbols | # Total of 2187 symbols checked Signed-off-by: Victor Toso <victortoso@xxxxxxxxxx> --- scripts/version-quest.py | 190 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 scripts/version-quest.py diff --git a/scripts/version-quest.py b/scripts/version-quest.py new file mode 100644 index 0000000000..01e5d28c59 --- /dev/null +++ b/scripts/version-quest.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 +# +# This is a simple utitly script to help check version of exported +# types in XML built with scripts/apibuild.py +# +# See Copyright for the status of this software. +# +# victortoso@xxxxxxxxxx +# + +import os +import re +import argparse + +tags = [] +includedir = "" +xmldir = "" +next_release = "v8.3.0" + +allowlist = { + 'virDomainSetBlockThreshold': '3.2.0', + 'virGetLastErrorMessage': '1.0.5.2', + 'virNodeDeviceCreate': '0.5.0', + 'virAdmClientClose': '1.3.5', + 'virAdmClientFree': '1.3.5', + 'virAdmClientGetID': '1.3.5', + 'virAdmClientGetInfo': '1.3.5', + 'virAdmClientGetTimestamp': '1.3.5', + 'virAdmClientGetTransport': '1.3.5', + 'virAdmConnectClose': '1.2.17', + 'virAdmConnectGetLibVersion': '1.3.1', + 'virAdmConnectGetURI': '1.3.1', + 'virAdmConnectIsAlive': '1.3.1', + 'virAdmConnectListServers': '1.3.2', + 'virAdmConnectLookupServer': '1.3.3', + 'virAdmConnectOpen': '1.2.17', + 'virAdmConnectRef': '1.2.17', + 'virAdmConnectRegisterCloseCallback': '1.3.1', + 'virAdmConnectUnregisterCloseCallback': '1.3.1', + 'virAdmGetVersion': '1.3.0', + 'virAdmServerFree': '1.3.2', + 'virAdmServerGetClientLimits': '1.3.5', + 'virAdmServerGetName': '1.3.2', + 'virAdmServerGetThreadPoolParameters': '1.3.4', + 'virAdmServerListClients': '1.3.5', + 'virAdmServerLookupClient': '1.3.5', + 'virAdmServerSetClientLimits': '1.3.5', + 'virAdmServerSetThreadPoolParameters': '1.3.4', + 'virAdmServerUpdateTlsFiles': '6.2.0', + 'virConnectFindStoragePoolSources': '0.4.6', + 'virConnectNumOfDefinedDomains': '0.1.6', + 'virConnectOpenAuth': '0.4.1', + 'virDomainBlockPeek': '0.4.4', + 'virDomainMemoryPeek': '0.4.4', + 'virNetworkUpdate': '1.0.0', + 'virConnectClose': '0.0.1', + 'virConnectGetType': '0.0.1', + 'virConnectGetVersion': '0.0.1', + 'virConnectListDomains': '0.0.1', + 'virConnectNumOfDomains': '0.0.1', + 'virConnectOpen': '0.0.1', + 'virConnectOpenReadOnly': '0.0.1', + 'virDomainCreateLinux': '0.0.1', + 'virDomainDestroy': '0.0.1', + 'virDomainFree': '0.0.1', + 'virDomainGetID': '0.0.1', + 'virDomainGetInfo': '0.0.1', + 'virDomainGetMaxMemory': '0.0.1', + 'virDomainGetName': '0.0.1', + 'virDomainGetOSType': '0.0.1', + 'virDomainGetXMLDesc': '0.0.1', + 'virDomainLookupByID': '0.0.1', + 'virDomainLookupByName': '0.0.1', + 'virDomainRestore': '0.0.2', + 'virDomainResume': '0.0.1', + 'virDomainSave': '0.0.2', + 'virDomainSetMaxMemory': '0.0.1', + 'virDomainShutdown': '0.0.1', + 'virDomainSuspend': '0.0.1', + 'virGetVersion': '0.0.1', +} + + +def get_symbols(xmlpath: str): + symbols = [] + expression = "<(.*?) name='(.*?)'.*version='(.*?)'" + with open(xmlpath) as file: + for line in file: + r = re.search(expression, line) + if r is not None: + symbols.append(r.groups()) + + return symbols + + +def find_version(symbol, start_version): + index = start_version == "" and -1 or tags.index(start_version) + for i in range(index + 1, len(tags)): + if git_check_symbol(symbol, tags[i]): + return tags[i] + + assert False + + +def get_tags_array() -> list[str]: + # We will be looking symbols at released tags. Only vx.y.z are + # interesting to us. + k = os.popen("git tag --list 'v*' | grep -v 'rc' | sort -V") + alltags = k.read().split() + return alltags + + +def git_check_symbol(symbol, version, full=False) -> bool: + path = full and "" or includedir + s = os.system(f"git grep -rqw {symbol} {version} {path}") + return os.waitstatus_to_exitcode(s) == 0 + + +def check_symbol(symbol, xml_version) -> (bool, str): + # For functions that were released with wrong sym version + if symbol in allowlist: + docversion = f"v{allowlist[symbol]}" + if docversion == xml_version: + return (False, f"{symbol} allowlist versions match: {xml_version}") + + if not git_check_symbol(symbol, docversion, True): + version = find_version(symbol, docversion) + return (False, f"{symbol} allowlist {docversion} => {version}") + + return (True, "") + + # Too recent, just skip. + if xml_version == next_release: + return (True, "") + + if xml_version not in tags: + return (False, f"{symbol}'s {xml_version} does not exist in git tags") + + index = tags.index(xml_version) + + if not git_check_symbol(symbol, tags[index]): + return (False, f"{symbol} does not exist in version {xml_version}") + + # Added in the first release. Seems fine. + if index == 0: + return (True, "") + + # Ooops. Symbol was found in the previous release too. + if git_check_symbol(symbol, tags[index - 1]): + version = find_version(symbol, "") + return (False, f"{symbol} {tags[index]} => real: {version}") + + # Added in the last tag, nothing else to do. + if index == len(tags) - 1: + return (True, "") + + # Ooops. Symbol was not found in the next release. + if not git_check_symbol(symbol, tags[index + 1]): + return (False, f"{symbol} in {tags[index]} but not {tags[index + 1]}") + + return (True, "") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Quest: Versions!") + parser.add_argument("-d", "--xmldir", type=str, + help="Directory of libvirt-.*.xml APIs") + parser.add_argument("-i", "--includedir", type=str, + help="Include directory of libvirt") + + args = parser.parse_args() + includedir = args.includedir + xmldir = args.xmldir + tags = get_tags_array() + + counter = {} + xmlfiles = ["libvirt", "libvirt-qemu", "libvirt-lxc", "libvirt-admin"] + for xml in xmlfiles: + symbols = get_symbols(f"{xmldir}/{xml}-api.xml") + counter[xml] = len(symbols) + for (symbol_type, symbol_name, symbol_version) in symbols: + ok, errmsg = check_symbol(symbol_name, f"v{symbol_version}") + if not ok: + print(f"{xml}: {symbol_type}: Failed: {errmsg}") + + total = 0 + for xml in counter.keys(): + total += counter[xml] + print(f"# {xml} has {counter[xml]} symbols") + print(f"# Total of {total} symbols checked") -- 2.35.1