Merge pull request #95 from tyll/update2

Make connections volatile instead of removing them
This commit is contained in:
Till Maas 2019-01-10 22:11:09 +01:00 committed by GitHub
commit f5ee8a7e1c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 96 additions and 33 deletions

View file

@ -5,6 +5,7 @@
import functools
import os
import socket
import time
import traceback
# pylint: disable=import-error, no-name-in-module
@ -983,18 +984,14 @@ class NMUtil:
def connection_delete(self, connection, timeout=10):
c_uuid = connection.get_uuid()
# Do nothing, if the connection is already gone
if connection not in self.connection_list():
return
def delete_cb(connection, result, cb_args):
success = False
try:
success = connection.delete_finish(result)
except Exception as e:
if Util.error_is_cancelled(e):
return
cb_args["error"] = str(e)
cb_args["success"] = success
Util.GMainLoop().quit()
if "update2" in dir(connection):
return self.volatilize_connection(connection, timeout)
delete_cb = Util.create_callback("delete_finish")
cancellable = Util.create_cancellable()
cb_args = {}
@ -1010,24 +1007,69 @@ class NMUtil:
# workaround libnm oddity. The connection may not yet be gone if the
# connection was active and is deactivating. Wait.
wait_count = 0
while True:
connections = self.connection_list(uuid=c_uuid)
if not connections:
return
wait_count += 1
if wait_count > 10:
break
import time
c_uuid = connection.get_uuid()
gone = self.wait_till_connection_is_gone(c_uuid)
if not gone:
raise MyError(
"connection %s was supposedly deleted successfully, but it's still here"
% (c_uuid)
)
time.sleep(1)
Util.GMainLoop_iterate_all()
def volatilize_connection(self, connection, timeout=10):
update2_cb = Util.create_callback("update2_finish")
raise MyError(
"connection %s was supposedly deleted successfully, but it's still here"
% (c_uuid)
cancellable = Util.create_cancellable()
cb_args = {}
connection.update2(
None, # settings
Util.NM().SettingsUpdate2Flags.IN_MEMORY_ONLY
| Util.NM().SettingsUpdate2Flags.VOLATILE, # flags
None, # args
cancellable,
update2_cb,
cb_args,
)
if not Util.GMainLoop_run(timeout):
cancellable.cancel()
raise MyError("failure to volatilize connection: %s" % ("timeout"))
Util.GMainLoop_iterate_all()
# Do not check of success if the connection does not exist anymore This
# can happen if the connection was already volatile and set to down
# during the module call
if connection not in self.connection_list():
return
# update2_finish returns None on failure and a GLib.Variant of type
# a{sv} with the result otherwise (which can be empty)
if cb_args.get("success", None) is None:
raise MyError(
"failure to volatilize connection: %s: %r"
% (cb_args.get("error", "unknown error"), cb_args)
)
def wait_till_connection_is_gone(self, uuid, timeout=10):
"""
Wait until a connection is gone or until the timeout elapsed
:param uuid: UUID of the connection that to wait for to be gone
:param timeout: Timeout in seconds to wait for
:returns: True when connection is gone, False when timeout elapsed
:rtype: bool
"""
def _poll_timeout_cb(unused):
if not self.connection_list(uuid=uuid):
Util.GMainLoop().quit()
poll_timeout_id = Util.GLib().timeout_add(100, _poll_timeout_cb, None)
gone = Util.GMainLoop_run(timeout)
Util.GLib().source_remove(poll_timeout_id)
return gone
def connection_activate(self, connection, timeout=15, wait_time=None):
already_retried = False
@ -1073,7 +1115,6 @@ class NMUtil:
)
already_retried = True
import time
time.sleep(1)
@ -1776,7 +1817,7 @@ class Cmd_nm(Cmd):
# found
if not con_cur:
self.log_error(
idx, "Connection not found on system and 'type' not present"
idx, "Connection not found on system and 'type' not specified"
)
return

View file

@ -96,20 +96,19 @@ class Util:
return True
GLib = cls.GLib()
result = []
timeout_reached = []
loop = cls.GMainLoop()
def _timeout_cb(unused):
result.append(1)
timeout_reached.append(1)
loop.quit()
return False
timeout_id = GLib.timeout_add(int(timeout * 1000), _timeout_cb, None)
loop.run()
if result:
return False
GLib.source_remove(timeout_id)
return True
if not timeout_reached:
GLib.source_remove(timeout_id)
return not timeout_reached
@classmethod
def GMainLoop_iterate(cls, may_block=False):
@ -126,6 +125,29 @@ class Util:
def create_cancellable(cls):
return cls.Gio().Cancellable.new()
@classmethod
def create_callback(cls, finish_method):
"""
Create a callback that will return the result of the finish method and
quit the GMainLoop
:param finish_method str: Name of the finish method to call from the
source object in the callback
"""
def callback(source_object, res, user_data):
success = None
try:
success = getattr(source_object, finish_method)(res)
except Exception as e:
if cls.error_is_cancelled(e):
return
user_data["error"] = str(e)
user_data["success"] = success
cls.GMainLoop().quit()
return callback
@classmethod
def error_is_cancelled(cls, e):
GLib = cls.GLib()