[PATCH v3 30/30] do-not-commit: script to validate output

Victor Toso victortoso at redhat.com
Wed Apr 20 19:08:19 UTC 2022


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 at redhat.com>
---
 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 at redhat.com
+#
+
+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



More information about the libvir-list mailing list