From 1372848511cb896b80b51ed1a3e9606bd9816631 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Fri, 10 Feb 2023 11:50:34 +0100 Subject: New upstream version 1.42.0 --- examples/python/gi/gmaincontext.py | 130 ++++++++++++++++----------- examples/python/gi/ovs-external-ids.py | 157 +++++++++++++++++++++------------ 2 files changed, 181 insertions(+), 106 deletions(-) (limited to 'examples/python/gi') 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" = ' % (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" = ' % (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"]) -- cgit 1.3.0-6-gf8a5