commit 7eac2854b2bfd504a99a4f0cdcefd41c52fe73c4 Author: Jiri Pirko jiri@resnulli.us Date: Tue Sep 11 17:02:26 2012 +0200
introduce command context
In this context, remember all background running commands. Kill them on cleanup.
Signed-off-by: Jiri Pirko jiri@resnulli.us
NetTest/NetTestCommand.py | 172 ++++++++++++++++++++++++----------------- NetTest/NetTestController.py | 7 +- NetTest/NetTestSlave.py | 18 +++-- 3 files changed, 117 insertions(+), 80 deletions(-) --- diff --git a/NetTest/NetTestCommand.py b/NetTest/NetTestCommand.py index e89f606..4fde1ee 100644 --- a/NetTest/NetTestCommand.py +++ b/NetTest/NetTestCommand.py @@ -37,42 +37,90 @@ class CommandException(Exception): def __str__(self): return "CommandException: " + str(self.command)
-class BgProcessException(Exception): - """Base class for client errors.""" +class BgCommandException(Exception): + """Base class for background command errors.""" def __init__(self, str): self._str = str
def __str__(self): - return "BgProcessError: " + self._str - -class BGProcesses: - def __init__(self): - self._dict = {} - - def add(self, bg_id, pid, pipe): - if bg_id in self._dict: - raise Exception - self._dict[bg_id] = {"pid": pid, "pipe": pipe} + return "BgCommandError: " + self._str
- def get_pid(self, bg_id): - return self._dict[bg_id]["pid"] +class BgCommand: + def __init__(self, bg_id, cmd_cls): + self._bg_id = bg_id + self._cmd_cls = cmd_cls + self._pid = None + self._read_pipe = None
- def get_pipe(self, bg_id): - return self._dict[bg_id]["pipe"] + def get_bg_id(self): + return self._bg_id
- def remove(self, bg_id): - del self._dict[bg_id] + def run(self): + read_pipe, write_pipe = os.pipe() + self._pid = os.fork() + if self._pid: + os.close(write_pipe) + logging.debug("Running in background with" + " id "%s", pid "%d"" % (self._bg_id, self._pid)) + self._read_pipe = read_pipe + return {"passed": True} + os.close(read_pipe) + os.setpgrp() + self._cmd_cls.set_handle_intr() + try: + self._cmd_cls.run() + result = self._cmd_cls.get_result() + except KeyboardInterrupt: + result = self._cmd_cls.get_result() + except: + type, value, tb = sys.exc_info() + result = {"Exception": ''.join(traceback.format_exception(type, value, tb))} + tmp = pickle.dumps(result) + os.write(write_pipe, tmp) + os.close(write_pipe) + os._exit(0) + + def wait_for(self): + logging.debug("Waiting for background command with id "%s", pid "%d"" % (self._bg_id, self._pid)) + os.waitpid(self._pid, 0) + + def interrupt(self): + logging.debug("Interrupting background command with id "%s", pid "%d"" % (self._bg_id, self._pid)) + os.killpg(os.getpgid(self._pid), signal.SIGINT) + os.waitpid(self._pid, 0) + + def kill(self): + logging.debug("Killing background command with id "%s", pid "%d"" % (self._bg_id, self._pid)) + os.killpg(os.getpgid(self._pid), signal.SIGKILL)
- def get_bg_process_result(self, bg_id): - pipe = self.get_pipe(bg_id) - tmp = os.read(pipe, 4096*10) + def get_result(self): + tmp = os.read(self._read_pipe, 4096*10) result = pickle.loads(tmp) if "Exception" in result: - raise BgProcessException(result["Exception"]) - os.close(pipe) + raise BgCommandException(result["Exception"]) + os.close(self._read_pipe) return result
-bg_processes = BGProcesses() +class NetTestCommandContext: + def __init__(self): + self._dict = {} + + def add_bg_cmd(self, bg_cmd): + self._dict[bg_cmd.get_bg_id()] = bg_cmd + + def del_bg_cmd(self, bg_cmd): + del self._dict[bg_cmd.get_bg_id()] + + def get_bg_cmd(self, bg_id): + return self._dict[bg_id] + + def _kill_all_bg_cmds(self): + for bg_id in self._dict: + self._dict[bg_id].kill() + + def cleanup(self): + self._kill_all_bg_cmds() + self._dict = {}
def NetTestCommandTest(command): test_name = command["value"] @@ -174,48 +222,49 @@ class NetTestCommandSystemConfig(NetTestCommandGeneric): res["res_data"] = res_data self.set_result(res)
-class NetTestCommandWait(NetTestCommandGeneric): +class NetTestCommandControl(NetTestCommandGeneric): + def __init__(self, command_context, command): + self._command_context = command_context + NetTestCommandGeneric.__init__(self, command) + +class NetTestCommandWait(NetTestCommandControl): def run(self): bg_id = self._command["value"] - pid = bg_processes.get_pid(bg_id) - logging.debug("Waiting for background id "%s", pid "%d"" % (bg_id, pid)) - os.waitpid(pid, 0) - result = bg_processes.get_bg_process_result(bg_id) - bg_processes.remove(bg_id) + bg_cmd = self._command_context.get_bg_cmd(bg_id) + bg_cmd.wait_for() + result = bg_cmd.get_result() + self._command_context.del_bg_cmd(bg_cmd) self.set_result(result)
-class NetTestCommandIntr(NetTestCommandGeneric): +class NetTestCommandIntr(NetTestCommandControl): def run(self): bg_id = self._command["value"] - pid = bg_processes.get_pid(bg_id) - logging.debug("Interrupting background id "%s", pid "%d"" % (bg_id, pid)) - os.killpg(os.getpgid(pid), signal.SIGINT) - os.waitpid(pid, 0) - result = bg_processes.get_bg_process_result(bg_id) - bg_processes.remove(bg_id) + bg_cmd = self._command_context.get_bg_cmd(bg_id) + bg_cmd.interrupt() + result = bg_cmd.get_result() + self._command_context.del_bg_cmd(bg_cmd) self.set_result(result)
-class NetTestCommandKill(NetTestCommandGeneric): +class NetTestCommandKill(NetTestCommandControl): def run(self): bg_id = self._command["value"] - pid = bg_processes.get_pid(bg_id) - logging.debug("Killing background id "%s", pid "%d"" % (bg_id, pid)) - os.killpg(os.getpgid(pid), signal.SIGKILL) - bg_processes.remove(bg_id) + bg_cmd = self._command_context.get_bg_cmd(bg_id) + bg_cmd.kill() + self._command_context.del_bg_cmd(bg_cmd) self.set_result({"passed": True})
-def get_command_class(command): +def get_command_class(command_context, command): cmd_type = command["type"] if cmd_type == "exec": return NetTestCommandExec(command) elif cmd_type == "test": return NetTestCommandTest(command) elif cmd_type == "wait": - return NetTestCommandWait(command) + return NetTestCommandWait(command_context, command) elif cmd_type == "intr": - return NetTestCommandIntr(command) + return NetTestCommandIntr(command_context, command) elif cmd_type == "kill": - return NetTestCommandKill(command) + return NetTestCommandKill(command_context, command) elif cmd_type == "system_config": return NetTestCommandSystemConfig(command) else: @@ -223,37 +272,18 @@ def get_command_class(command): raise Exception("Unknown command type "%s"" % cmd_type)
class NetTestCommand: - def __init__(self, command): - self._command_class = get_command_class(command) + def __init__(self, command_context, command): + self._command_class = get_command_class(command_context, command) + self._command_context = command_context self._command = command
def run(self): cmd_cls = self._command_class if "bg_id" in self._command: bg_id = self._command["bg_id"] - read_pipe, write_pipe = os.pipe() - pid = os.fork() - if pid: - os.close(write_pipe) - logging.debug("Running in background with" - " id "%s", pid "%d"" % (bg_id, pid)) - bg_processes.add(bg_id, pid, read_pipe) - return {"passed": True} - os.close(read_pipe) - os.setpgrp() - cmd_cls.set_handle_intr() - try: - cmd_cls.run() - result = cmd_cls.get_result() - except KeyboardInterrupt: - result = cmd_cls.get_result() - except: - type, value, tb = sys.exc_info() - result = {"Exception": ''.join(traceback.format_exception(type, value, tb))} - tmp = pickle.dumps(result) - os.write(write_pipe, tmp) - os.close(write_pipe) - os._exit(0) + bg_cmd = BgCommand(bg_id, cmd_cls) + self._command_context.add_bg_cmd(bg_cmd) + return bg_cmd.run() else: cmd_cls.run() return cmd_cls.get_result() diff --git a/NetTest/NetTestController.py b/NetTest/NetTestController.py index 2a6bbd6..01a5f9b 100644 --- a/NetTest/NetTestController.py +++ b/NetTest/NetTestController.py @@ -22,7 +22,7 @@ from NetTest.NetTestParse import NetTestParse from Common.SlaveUtils import prepare_client_session from Common.NetUtils import get_corespond_local_ip, MacPool from NetTest.NetTestSlave import DefaultRPCPort -from NetTest.NetTestCommand import NetTestCommand, str_command +from NetTest.NetTestCommand import NetTestCommandContext, NetTestCommand, str_command from Common.LoggingServer import LoggingServer from Common.VirtUtils import VirtNetCtl, VirtDomainCtl, BridgeCtl from Common.Utils import wait_for @@ -43,7 +43,7 @@ class NetTestController: self._docleanup = cleanup self._res_serializer = res_serializer self._remote_capture_files = {} - + self._command_context = NetTestCommandContext() self._machine_pool = MachinePool([])
self._recipe = {} @@ -314,7 +314,7 @@ class NetTestController: pass
if machine_id == "0": - cmd_res = NetTestCommand(command).run() + cmd_res = NetTestCommand(self._command_context, command).run() else: rpc = self._get_machinerpc(machine_id) if "timeout" in command: @@ -390,6 +390,7 @@ class NetTestController:
self._deconfigure_slaves() self._disconnect_slaves() + self._command_context.cleanup()
if not err: return res diff --git a/NetTest/NetTestSlave.py b/NetTest/NetTestSlave.py index 8065591..7c391f8 100644 --- a/NetTest/NetTestSlave.py +++ b/NetTest/NetTestSlave.py @@ -20,7 +20,7 @@ from Common.XmlRpc import Server from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler from NetConfig.NetConfig import NetConfig from NetConfig.NetConfigDevice import NetConfigDeviceAllCleanup -from NetTest.NetTestCommand import NetTestCommand, CommandException +from NetTest.NetTestCommand import NetTestCommandContext, NetTestCommand, CommandException from Common.Utils import die_when_parent_die from Common.NetUtils import scan_netdevs
@@ -30,10 +30,11 @@ class NetTestSlaveXMLRPC: ''' Exported xmlrpc methods ''' - def __init__(self): + def __init__(self, command_context): self._netconfig = None self._packet_captures = {} self._netconfig = NetConfig() + self._command_context = command_context
def hello(self): return "hello" @@ -112,7 +113,7 @@ class NetTestSlaveXMLRPC:
def run_command(self, command): try: - return NetTestCommand(command).run() + return NetTestCommand(self._command_context, command).run() except: log_exc_traceback() raise CommandException(command) @@ -120,11 +121,13 @@ class NetTestSlaveXMLRPC: def machine_cleanup(self): NetConfigDeviceAllCleanup() self._netconfig.cleanup() + self._command_context.cleanup() return True
class MySimpleXMLRPCServer(Server): - def __init__(self, *args, **kwargs): + def __init__(self, command_context, *args, **kwargs): self._finished = False + self._command_context = command_context Server.__init__(self, *args, **kwargs)
def register_die_signal(self, signum): @@ -138,6 +141,7 @@ class MySimpleXMLRPCServer(Server): while True: try: if self._finished: + self._command_context.cleanup() import sys sys.exit() return @@ -149,12 +153,14 @@ class NetTestSlave: def __init__(self, port = DefaultRPCPort): die_when_parent_die()
- server = MySimpleXMLRPCServer(("", port), SimpleXMLRPCRequestHandler, + command_context = NetTestCommandContext() + server = MySimpleXMLRPCServer(command_context, + ("", port), SimpleXMLRPCRequestHandler, logRequests = False) server.register_die_signal(signal.SIGHUP) server.register_die_signal(signal.SIGINT) server.register_die_signal(signal.SIGTERM) - server.register_instance(NetTestSlaveXMLRPC()) + server.register_instance(NetTestSlaveXMLRPC(command_context)) self._server = server
def run(self):
lnst-developers@lists.fedorahosted.org