about summary refs log tree commit diff
path: root/examples/python
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2023-03-09 20:01:53 +0100
committerMichael Biebl <biebl@debian.org>2023-03-09 20:01:53 +0100
commitcffb548b401e2cd3fad166aa92e0f634bbb1d4d6 (patch)
tree6d803d8f7d0d92a1c5e5705974fff8975472af95 /examples/python
parent647d6b95d5475e0a1bf3d6f299d1cb8c9041a7a4 (diff)
parent12e1ee797bbf0b10d1fd5afd4b947484769f2b9f (diff)
Merge tag 'debian/1.42.4-1' into debian/bullseye-backports
network-manager Debian release 1.42.4-1
Diffstat (limited to 'examples/python')
-rwxr-xr-xexamples/python/gi/gmaincontext.py130
-rwxr-xr-xexamples/python/gi/ovs-external-ids.py157
2 files changed, 181 insertions, 106 deletions
diff --git a/examples/python/gi/gmaincontext.py b/examples/python/gi/gmaincontext.py
index 90a9fa25..64a5620b 100755
--- a/examples/python/gi/gmaincontext.py
+++ b/examples/python/gi/gmaincontext.py
@@ -36,7 +36,6 @@
 
 ###############################################################################
 
-import os
 import sys
 import time
 import traceback
@@ -260,7 +259,6 @@ def create_nmc(dbus_connection):
     # This actually should not happen. There is no other reason why
     # initialization can fail.
     assert False, "NMClient initialization is not supposed to fail"
-    return nmc
 
 
 ###############################################################################
@@ -297,12 +295,11 @@ def make_call(nmc):
                 else:
                     log(f"[make_call]: dbus_call() completed with error: {e}")
 
-                if False:
-                    # I don't understand why, but if you hit this exception (e.g. by setting a low
-                    # timeout) and pass the exception to the out context, then an additional reference
-                    # to nmc is leaked, and destroy_nmc() will fail. Workaround
-                    r.error = e
-
+                # I don't understand why, but if you hit this exception (e.g. by setting a low
+                # timeout) and pass the exception to the out context, then an additional reference
+                # to nmc is leaked, and destroy_nmc() will fail. Workaround
+                #
+                # r.error = e
                 r.error = str(e)
             else:
                 log(
@@ -329,7 +326,7 @@ def make_call(nmc):
 ###############################################################################
 
 
-def destroy_nmc(nmc_holder):
+def destroy_nmc(nmc_holder, destroy_mode):
     # The way to shutdown an NMClient is just by unrefing it.
     #
     # At any moment, can an NMClient instance have pending async operations.
@@ -362,53 +359,77 @@ def destroy_nmc(nmc_holder):
     (nmc,) = nmc_holder
     nmc_holder.clear()
 
-    if not nmc:
-        log(f"[destroy_nmc]: nothing to destroy")
-        return
-
     log(
-        f"[destroy_nmc]: destroying NMClient {nmc}: pyref={sys.getrefcount(nmc)}, ref_count={nmc.ref_count}"
+        f"[destroy_nmc]: destroying NMClient {nmc}: pyref={sys.getrefcount(nmc)}, ref_count={nmc.ref_count}, destroy_mode={destroy_mode}"
     )
 
-    ctx = nmc.get_main_context()
+    if destroy_mode == 0:
+        ctx = nmc.get_main_context()
 
-    finished = []
+        finished = []
 
-    def _weak_ref_cb():
-        log(f"[destroy_nmc]: context busy watcher is gone")
-        finished.clear()
-        finished.append(True)
+        def _weak_ref_cb():
+            log(f"[destroy_nmc]: context busy watcher is gone")
+            finished.clear()
+            finished.append(True)
 
-    # We take a weak ref on the context-busy-watcher object and give up
-    # our reference on nmc. This must be the last reference, which initiates
-    # the shutdown of the NMClient.
-    weak_ref = nmc.get_context_busy_watcher().weak_ref(_weak_ref_cb)
-    del nmc
+        # We take a weak ref on the context-busy-watcher object and give up
+        # our reference on nmc. This must be the last reference, which initiates
+        # the shutdown of the NMClient.
+        weak_ref = nmc.get_context_busy_watcher().weak_ref(_weak_ref_cb)
+        del nmc
 
-    def _timeout_cb(unused):
-        if not finished:
-            # Somebody else holds a reference to the NMClient and keeps
-            # it alive. We cannot properly clean up.
-            log(
-                f"[destroy_nmc]: ERROR: timeout waiting for context busy watcher to be gone"
-            )
-            finished.append(False)
-        return False
+        def _timeout_cb(unused):
+            if not finished:
+                # Somebody else holds a reference to the NMClient and keeps
+                # it alive. We cannot properly clean up.
+                log(
+                    f"[destroy_nmc]: ERROR: timeout waiting for context busy watcher to be gone"
+                )
+                finished.append(False)
+            return False
 
-    timeout_source = GLib.timeout_source_new(1000)
-    timeout_source.set_callback(_timeout_cb)
-    timeout_source.attach(ctx)
+        timeout_source = GLib.timeout_source_new(1000)
+        timeout_source.set_callback(_timeout_cb)
+        timeout_source.attach(ctx)
 
-    while not finished:
-        log(f"[destroy_nmc]: iterating main context")
-        ctx.iteration(True)
+        while not finished:
+            log(f"[destroy_nmc]: iterating main context")
+            ctx.iteration(True)
 
-    timeout_source.destroy()
+        timeout_source.destroy()
 
-    log(f"[destroy_nmc]: done: {finished[0]}")
-    if not finished[0]:
-        weak_ref.unref()
-        raise Exception("Failure to destroy NMClient: something keeps it alive")
+        log(f"[destroy_nmc]: done: {finished[0]}")
+        if not finished[0]:
+            weak_ref.unref()
+            raise Exception("Failure to destroy NMClient: something keeps it alive")
+
+    else:
+
+        if destroy_mode == 1:
+            ctx = GLib.MainContext.default()
+        else:
+            # Run the maincontext of the NMClient.
+            ctx = nmc.get_main_context()
+        with MainLoopRun("destroy_nmc", ctx, 2) as r:
+
+            def _wait_shutdown_cb(source_unused, result, r):
+                try:
+                    NM.Client.wait_shutdown_finish(result)
+                except Exception as e:
+                    if error_is_cancelled(e):
+                        log(
+                            f"[destroy_nmc]: wait_shutdown() completed with cancellation after timeout"
+                        )
+                    else:
+                        log(f"[destroy_nmc]: wait_shutdown() completed with error: {e}")
+                else:
+                    log(f"[destroy_nmc]: wait_shutdown() completed with success")
+
+                r.quit()
+
+            nmc.wait_shutdown(True, r.cancellable, _wait_shutdown_cb, r)
+            del nmc
 
 
 ###############################################################################
@@ -425,18 +446,25 @@ def run1():
         make_call(nmc)
         log()
 
-        # To cleanup the NMClient, we need to give up the reference. Move
-        # it to a list, and destroy_nmc() will take care of it.
-        nmc_holder = [nmc]
-        del nmc
-        destroy_nmc(nmc_holder)
+        if not nmc:
+            log(f"[destroy_nmc]: nothing to destroy")
+        else:
+            # To cleanup the NMClient, we need to give up the reference. Move
+            # it to a list, and destroy_nmc() will take care of it.
+            nmc_holder = [nmc]
+            del nmc
+
+            # In the example, there are three modes how the destroy is
+            # implemented.
+            destroy_nmc(nmc_holder, destroy_mode=1)
+
         log()
         log("done")
     except Exception as e:
         log()
         log("EXCEPTION:")
         log(f"{e}")
-        for tb in traceback.format_exception(e):
+        for tb in traceback.format_exception(None, e, e.__traceback__):
             for l in tb.split("\n"):
                 log(f">>> {l}")
         return False
diff --git a/examples/python/gi/ovs-external-ids.py b/examples/python/gi/ovs-external-ids.py
index 3bc9de8f..fe4fbd9d 100755
--- a/examples/python/gi/ovs-external-ids.py
+++ b/examples/python/gi/ovs-external-ids.py
@@ -5,14 +5,15 @@
 #
 
 #
-# set and show OVS external-ids for a connection:
+# set and show OVS external-ids and other-config for a connection:
 #
 
-import sys
+import collections
 import os
-import re
 import pprint
+import re
 import subprocess
+import sys
 
 import gi
 
@@ -178,14 +179,14 @@ def device_reapply(device, connection, version_id):
         raise result_error[0]
 
 
-def ovs_print_external_ids(prefix):
+def ovs_print_config(prefix):
     if not can_sudo():
         _print(prefix + ": not running as root and cannot call ovs-vsctl")
         return
 
     cmds = [["ovs-vsctl", "show"]]
     for typ in ["Bridge", "Port", "Interface"]:
-        cmds += [["ovs-vsctl", "--columns=name,external-ids", "list", typ]]
+        cmds += [["ovs-vsctl", "--columns=name,external-ids,other-config", "list", typ]]
 
     out = ""
     for cmd in cmds:
@@ -207,7 +208,11 @@ def usage():
     )
     _print("   DEVICE :=  [iface] STRING")
     _print("   GETTER  := ( KEY | ~REGEX_KEY )  [... GETTER]")
-    _print("   SETTER  := ( + | - | -KEY | [+]KEY VALUE ) [... SETTER]")
+    _print("   SETTER  := ( +[e:|o:] | -[e:|o:] | -KEY | [+]KEY VALUE ) [... SETTER]")
+    _print("")
+    _print(
+        'Prefix KEY with "e:" or "o:" to set external-ids or other-config ("e:" is the default)'
+    )
 
 
 def die(msg, show_usage=False):
@@ -221,6 +226,24 @@ def die_usage(msg):
     die(msg, show_usage=True)
 
 
+DataTypeTuple = collections.namedtuple(
+    "DataTypeTuple", ["short", "name", "setting_type", "property_name"]
+)
+
+DataTypeE = DataTypeTuple(
+    "external-ids",
+    "ovs-external-ids",
+    NM.SettingOvsExternalIDs,
+    NM.SETTING_OVS_EXTERNAL_IDS_DATA,
+)
+DataTypeO = DataTypeTuple(
+    "other-config",
+    "ovs-other-config",
+    NM.SettingOvsOtherConfig,
+    NM.SETTING_OVS_OTHER_CONFIG_DATA,
+)
+
+
 def parse_args(argv):
     args = {
         "mode": MODE_GET,
@@ -274,12 +297,14 @@ def parse_args(argv):
             continue
 
         if not a:
-            die_usage("argument should specify a external-id but is empty string")
+            die_usage(
+                "argument should specify a external-id/other-config but is empty string"
+            )
 
         if a[0] == "-":
             v = (a, None)
             i += 1
-        elif a == "+":
+        elif a in ["+", "+o:", "+e:"]:
             v = (a, None)
             i += 1
         else:
@@ -294,7 +319,7 @@ def parse_args(argv):
 
     if args["mode"] == MODE_SET:
         if not args["ids_arg"]:
-            die_usage("Requires one or more external-ids to set or delete")
+            die_usage("Requires one or more external-ids/other-config to set or delete")
 
     return args
 
@@ -319,9 +344,6 @@ def devices_filter(devices, select_arg):
     devices = list(sorted(devices, key=device_to_str))
     if not select_arg:
         return devices
-    # we preserve the order of the selected devices. And
-    # if devices are selected multiple times, we return
-    # them multiple times.
     l = []
     f = select_arg
     for d in devices:
@@ -342,9 +364,6 @@ def connections_filter(connections, select_arg):
     connections = list(sorted(connections, key=connection_to_str))
     if not select_arg:
         return connections
-    # we preserve the order of the selected connections. And
-    # if connections are selected multiple times, we return
-    # them multiple times.
     l = []
     f = select_arg
     for c in connections:
@@ -352,7 +371,7 @@ def connections_filter(connections, select_arg):
             if f[1] == c.get_id():
                 l.append(c)
         elif f[0] == "~id":
-            if re.match(f[1], c.get_id()):
+            if re.search(f[1], c.get_id()):
                 l.append(c)
         elif f[0] == "uuid":
             if f[1] == c.get_uuid():
@@ -361,7 +380,7 @@ def connections_filter(connections, select_arg):
             if f[1] == c.get_connection_type():
                 l.append(c)
         elif f[0] == "~type":
-            if re.match(f[1], c.get_connection_type()):
+            if re.search(f[1], c.get_connection_type()):
                 l.append(c)
         else:
             assert f[0] == "*"
@@ -384,7 +403,7 @@ def ids_select(ids, mode, ids_arg):
         if mode == MODE_GET:
             if d[0] == "~":
                 r = re.compile(d[1:])
-                keys.update([k for k in ids if r.match(k)])
+                keys.update([k for k in ids if r.search(k)])
             else:
                 keys.update([k for k in ids if k == d])
                 if d not in requested:
@@ -400,44 +419,67 @@ def ids_select(ids, mode, ids_arg):
 
 
 def connection_print(connection, mode, ids_arg, dbus_path, prefix=""):
-    sett = connection.get_setting(NM.SettingOvsExternalIDs)
-
-    if sett is not None:
-        all_ids = list(sett.get_data_keys())
-        keys, requested = ids_select(all_ids, mode, ids_arg)
-        num_str = "%s" % (len(all_ids))
-    else:
-        keys = []
-        requested = []
+    def _num_str(connection, data_type):
+        sett = connection.get_setting(data_type.setting_type)
         num_str = "none"
+        if sett is not None:
+            all_ids = list(sett.get_data_keys())
+            num_str = "%s" % (len(all_ids))
+        return num_str
 
     _print(
-        "%s%s [%s]" % (prefix, connection_to_str(connection, show_type=True), num_str)
+        "%s%s [e:%s, o:%s]"
+        % (
+            prefix,
+            connection_to_str(connection, show_type=True),
+            _num_str(connection, DataTypeE),
+            _num_str(connection, DataTypeO),
+        )
     )
     if dbus_path:
         _print("%s   %s" % (prefix, dbus_path))
-    if sett is not None:
-        dd = sett.get_property(NM.SETTING_OVS_EXTERNAL_IDS_DATA)
-    else:
-        dd = {}
-    for k in keys:
-        v = sett.get_data(k)
-        assert v is not None
-        assert v == dd.get(k, None)
-        _print('%s   "%s" = "%s"' % (prefix, k, v))
-    for k in requested:
-        _print('%s   "%s" = <unset>' % (prefix, k))
 
+    for data_type in [DataTypeE, DataTypeO]:
 
-def sett_update(connection, ids_arg):
+        sett = connection.get_setting(data_type.setting_type)
+        if sett is not None:
+            all_ids = list(sett.get_data_keys())
+            keys, requested = ids_select(all_ids, mode, ids_arg)
+        else:
+            keys = []
+            requested = []
 
-    sett = connection.get_setting(NM.SettingOvsExternalIDs)
+        if sett is not None:
+            dd = sett.get_property(data_type.property_name)
+        else:
+            dd = {}
+        for k in keys:
+            v = sett.get_data(k)
+            assert v is not None
+            assert v == dd.get(k, None)
+            _print('%s   %s: "%s" = "%s"' % (prefix, data_type.short, k, v))
+        for k in requested:
+            _print('%s   %s: "%s" = <unset>' % (prefix, data_type.short, k))
+
+
+def sett_update(connection, ids_arg):
 
     for d in ids_arg:
         op = d[0][0]
         key = d[0][1:]
         val = d[1]
 
+        if key == "o" or key.startswith("o:"):
+            data_type = DataTypeO
+            key = key[2:]
+        elif key == "e" or key.startswith("e:"):
+            data_type = DataTypeE
+            key = key[2:]
+        else:
+            data_type = DataTypeE
+
+        sett = connection.get_setting(data_type.setting_type)
+
         oldval = None
         if sett is not None:
             oldval = sett.get_data(key)
@@ -446,15 +488,17 @@ def sett_update(connection, ids_arg):
             assert val is None
             if key == "":
                 if sett is None:
-                    _print(" DEL: setting (ovs-external-ids group was not present)")
+                    _print(
+                        " DEL: setting (%s group was not present)" % (data_type.name,)
+                    )
                 else:
-                    connection.remove_setting(NM.SettingOvsExternalIDs)
+                    connection.remove_setting(data_type.setting_type)
                     sett = None
-                    _print(" DEL: setting")
+                    _print(" DEL: setting (%s)" % (data_type.name,))
                 continue
 
             if sett is None:
-                _print(' DEL: "%s" (ovs-external-ids group was not present)' % (key))
+                _print(' DEL: "%s" (%s group was not present)' % (key, data_type.name))
                 continue
             if oldval is None:
                 _print(' DEL: "%s" (id was unset)' % (key))
@@ -466,21 +510,22 @@ def sett_update(connection, ids_arg):
         if key == "":
             assert val is None
             if sett is None:
-                sett = NM.SettingOvsExternalIDs.new()
+                sett = data_type.setting_type.new()
                 connection.add_setting(sett)
-                _print(" SET: setting (external-ids group was added)")
+                _print(" SET: setting (%s group was added)" % (data_type.name,))
                 continue
 
-            _print(" SET: setting (external-ids group was present)")
+            _print(" SET: setting (%s group was present)" % (data_type.name,))
             continue
 
         assert val is not None
 
         if sett is None:
-            sett = NM.SettingOvsExternalIDs.new()
+            sett = data_type.setting_type.new()
             connection.add_setting(sett)
             _print(
-                ' SET: "%s" = "%s" (external-ids group was not present)' % (key, val)
+                ' SET: "%s" = "%s" (%s group was not present)'
+                % (key, val, data_type.name)
             )
         elif oldval is None:
             _print(' SET: "%s" = "%s" (new)' % (key, val))
@@ -579,7 +624,7 @@ def do_apply(nmc, device, ids_arg, do_test):
     )
     _print()
 
-    ovs_print_external_ids("BEFORE-OVS-VSCTL: ")
+    ovs_print_config("BEFORE-OVS-VSCTL: ")
     _print()
 
     connection = NM.SimpleConnection.new_clone(connection_orig)
@@ -619,7 +664,7 @@ def do_apply(nmc, device, ids_arg, do_test):
     )
     _print()
 
-    ovs_print_external_ids("AFTER-OVS-VSCTL: ")
+    ovs_print_config("AFTER-OVS-VSCTL: ")
 
 
 ###############################################################################
@@ -636,7 +681,7 @@ if __name__ == "__main__":
 
         if len(devices) != 1:
             _print(
-                "To apply the external-ids of a device, exactly one connection must be selected. Instead, %s devices matched ([%s])"
+                "To apply the external-ids/other-config of a device, exactly one connection must be selected. Instead, %s devices matched ([%s])"
                 % (len(devices), ", ".join([device_to_str(c) for c in devices]))
             )
             die_usage("Select unique device to apply")
@@ -649,7 +694,7 @@ if __name__ == "__main__":
         if args["mode"] == MODE_SET:
             if len(connections) != 1:
                 _print(
-                    "To set the external-ids of a connection, exactly one connection must be selected via id|uuid. Instead, %s connection matched ([%s])"
+                    "To set the external-ids/other-config of a connection, exactly one connection must be selected via id|uuid. Instead, %s connection matched ([%s])"
                     % (
                         len(connections),
                         ", ".join([connection_to_str(c) for c in connections]),
@@ -659,6 +704,8 @@ if __name__ == "__main__":
             do_set(nmc, connections[0], args["ids_arg"], do_test=args["do_test"])
         else:
             if len(connections) < 1:
-                _print("No connection selected for printing the external ids")
+                _print(
+                    "No connection selected for printing the external ids/other-config"
+                )
                 die_usage("Select connection to get")
             do_get(connections, args["ids_arg"])