class TestApplicationInstance(unittest.TestCase): def setUp(self): logger.DEBUG = '-v' in sys.argv self.temp_file = '/tmp/temp.txt' self.file_name = "/tmp/file_with_pid" self.inst = ApplicationInstance(os.path.abspath(self.file_name), False) self.subproc = None def tearDown(self): for f in (self.temp_file, self.file_name): if os.path.exists(f): os.remove(f) self.killProcess() def createProcess(self): dummy = 'dummy_proc.sh' dummyPath = os.path.join(os.path.dirname(__file__), dummy) self.subproc = subprocess.Popen(dummyPath) return self.subproc.pid def killProcess(self): if self.subproc: self.subproc.kill() self.subproc.wait() self.subproc = None def test_create_and_remove_pid_file(self): #create pid file self.inst.start_application() self.assertTrue(os.path.isfile(self.file_name)) #remove pid file self.inst.exit_application() self.assertFalse(os.path.isfile(self.file_name)) def test_write_pid_file(self): self.inst.start_application() #get pid/procname of current process this_pid = os.getpid() this_procname = tools.process_name(this_pid) with open(self.file_name, 'rt') as file_with_pid: self.assertEqual(file_with_pid.read(), '{}\n{}'.format(this_pid, this_procname)) def test_existing_process_with_correct_procname(self): """ Test the check function with an existing process with correct process name """ pid = self.createProcess() procname = tools.process_name(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) # Execute test self.assertFalse(self.inst.check()) def test_existing_process_with_correct_proc_cmdline(self): """ Test the check function with an existing process with correct process cmdline (for backwards compatibility) """ pid = self.createProcess() procname = tools.process_cmdline(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) # Execute test self.assertFalse(self.inst.check()) def test_no_pid_file(self): self.assertTrue(self.inst.check()) def test_existing_process_with_wrong_procname(self): """ Test the check function with an existing process with wrong process name """ pid = self.createProcess() procname = tools.process_name(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname + "DELETE") # Execute test self.assertTrue(self.inst.check()) def test_existing_process_with_wrong_pid(self): """ Test the check function with an existing process with wrong pid """ pid = self.createProcess() procname = tools.process_name(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write("987654321\n") file_with_pid.write(procname) # Execute test self.assertTrue(self.inst.check()) def test_killing_existing_process(self): """ Test the check function with an existing process with correct process name """ pid = self.createProcess() procname = tools.process_name(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) self.assertFalse(self.inst.check()) self.killProcess() # Execute test self.assertTrue(self.inst.check()) def test_non_existing_process(self): """ Test the check function with a non existing process """ # GIVE # # create file with fake pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write("987654321\n") file_with_pid.write("FAKE_PROCNAME") # Execute test self.assertTrue(self.inst.check()) def test_leftover_empty_lockfile(self): with open(self.file_name, 'wt')as f: pass self.assertTrue(self.inst.check()) def write_after_flock(self, pid_file): inst = ApplicationInstance(os.path.abspath(pid_file), auto_exit = False, flock = True) with open(self.temp_file, 'wt') as f: f.write('foo') inst.flockUnlock() def test_thread_write_without_flock(self): thread = Thread(target = self.write_after_flock, args = (self.file_name, )) thread.start() #wait for the thread to finish thread.join() self.assertTrue(os.path.exists(self.temp_file)) with open(self.temp_file, 'rt') as f: self.assertEqual(f.read(), 'foo') def test_flock_exclusive(self): self.inst.flockExclusiv() thread = Thread(target = self.write_after_flock, args = (self.file_name, )) thread.start() #give the thread some time thread.join(0.01) self.assertFalse(os.path.exists(self.temp_file)) self.inst.flockUnlock() #wait for the thread to finish thread.join() self.assertTrue(os.path.exists(self.temp_file)) with open(self.temp_file, 'rt') as f: self.assertEqual(f.read(), 'foo') def test_auto_flock(self): self.inst = ApplicationInstance(os.path.abspath(self.file_name), auto_exit = False, flock = True) thread = Thread(target = self.write_after_flock, args = (self.file_name, )) thread.start() #give the thread some time thread.join(0.01) self.assertFalse(os.path.exists(self.temp_file)) self.inst.start_application() #wait for the thread to finish thread.join() self.assertTrue(os.path.exists(self.temp_file)) with open(self.temp_file, 'rt') as f: self.assertEqual(f.read(), 'foo') def test_auto_exit_unique_process(self): self.inst = ApplicationInstance(os.path.abspath(self.file_name), auto_exit = True) self.assertTrue(os.path.exists(self.file_name)) this_pid = os.getpid() this_procname = tools.process_name(this_pid) with open(self.file_name, 'rt') as file_with_pid: self.assertEqual(file_with_pid.read(), '{}\n{}'.format(this_pid, this_procname)) def test_auto_exit_other_running_process(self): pid = self.createProcess() procname = tools.process_name(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) with self.assertRaises(SystemExit): self.inst = ApplicationInstance(os.path.abspath(self.file_name), auto_exit = True)
class TestApplicationInstance(unittest.TestCase): def setUp(self): logger.DEBUG = '-v' in sys.argv self.temp_file = '/tmp/temp.txt' self.file_name = "/tmp/file_with_pid" self.inst = ApplicationInstance(os.path.abspath(self.file_name), False) self.subproc = None def tearDown(self): for f in (self.temp_file, self.file_name): if os.path.exists(f): os.remove(f) if self.subproc: self.subproc.kill() self.subproc = None def createProcess(self): with open(self.temp_file, 'wt') as output: self.subproc = subprocess.Popen("top", stdout=output) return self.subproc.pid def getProcName(self, pid): with open('/proc/%s/cmdline' % pid, 'r') as file: return file.read().strip('\n') def test_create_and_remove_pid_file(self): #create pid file self.inst.start_application() self.assertTrue(os.path.isfile(self.file_name)) #remove pid file self.inst.exit_application() self.assertFalse(os.path.isfile(self.file_name)) def test_write_pid_file(self): self.inst.start_application() #get pid/procname of current process this_pid = os.getpid() this_procname = self.getProcName(this_pid) with open(self.file_name, 'rt') as file_with_pid: self.assertEqual(file_with_pid.read(), '%s\n%s' % (this_pid, this_procname)) def test_existing_process_with_correct_procname(self): """ Test the check function with an existing process with correct process name """ pid = self.createProcess() procname = self.getProcName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) # Execute test self.assertFalse(self.inst.check()) def test_existing_process_with_wrong_procname(self): """ Test the check function with an existing process with wrong process name """ pid = self.createProcess() procname = self.getProcName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname + "DELETE") # Execute test self.assertTrue(self.inst.check()) def test_existing_process_with_wrong_pid(self): """ Test the check function with an existing process with wrong pid """ pid = self.createProcess() procname = self.getProcName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write("987654321\n") file_with_pid.write(procname) # Execute test self.assertTrue(self.inst.check()) def test_killing_existing_process(self): """ Test the check function with an existing process with correct process name """ pid = self.createProcess() procname = self.getProcName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) #kill process self.subproc.kill() self.subproc.wait() self.subproc = None # Execute test self.assertTrue(self.inst.check()) def test_non_existing_process(self): """ Test the check function with a non existing process """ # GIVE # # create file with fake pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write("987654321\n") file_with_pid.write("FAKE_PROCNAME") # Execute test self.assertTrue(self.inst.check()) def test_leftover_empty_lockfile(self): with open(self.file_name, 'wt') as f: pass self.assertTrue(self.inst.check()) def write_after_flock( self, pid_file, ): inst = ApplicationInstance(os.path.abspath(pid_file), False) inst.flockExclusiv() with open(self.temp_file, 'wt') as f: f.write('foo') inst.flockUnlock() def test_thread_write_without_flock(self): thread = Thread(target=self.write_after_flock, args=(self.file_name, )) thread.start() #wait for the thread to finish thread.join() self.assertTrue(os.path.exists(self.temp_file)) with open(self.temp_file, 'rt') as f: self.assertEqual(f.read(), 'foo') def test_flock_exclusive(self): self.inst.flockExclusiv() thread = Thread(target=self.write_after_flock, args=(self.file_name, )) thread.start() #give the thread some time sleep(0.01) self.assertFalse(os.path.exists(self.temp_file)) self.inst.flockUnlock() #wait for the thread to finish thread.join() self.assertTrue(os.path.exists(self.temp_file)) with open(self.temp_file, 'rt') as f: self.assertEqual(f.read(), 'foo')
class Daemon: """ A generic daemon class. Usage: subclass the Daemon class and override the run() method Daemon Copyright by Sander Marechal License CC BY-SA 3.0 http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ """ def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/stdout', stderr='/dev/null'): self.stdin = stdin self.stdout = stdout self.stderr = stderr self.pidfile = pidfile self.appInstance = ApplicationInstance(pidfile, auto_exit = False, flock = False) def daemonize(self): """ do the UNIX double-fork magic, see Stevens' "Advanced Programming in the UNIX Environment" for details (ISBN 0201563177) http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 """ logger.debug('start', self) try: pid = os.fork() if pid > 0: # exit first parent sys.exit(0) except OSError as e: logger.error("fork #1 failed: %d (%s)" % (e.errno, str(e)), self) sys.exit(1) # decouple from parent environment os.chdir("/") os.setsid() os.umask(0) # do second fork try: pid = os.fork() if pid > 0: # exit from second parent sys.exit(0) except OSError as e: logger.error("fork #2 failed: %d (%s)" % (e.errno, str(e)), self) sys.exit(1) # redirect standard file descriptors sys.stdout.flush() sys.stderr.flush() si = open(self.stdin, 'r') so = open(self.stdout, 'w') se = open(self.stderr, 'w') os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) # write pidfile logger.debug('write pidfile', self) atexit.register(self.appInstance.exit_application) signal.signal(signal.SIGTERM, self._cleanup_handler) self.appInstance.start_application() def _cleanup_handler(self, signum, frame): self.fifo.delfifo() self.appInstance.exit_application() sys.exit(0) def start(self): """ Start the daemon """ # Check for a pidfile to see if the daemon already runs if not self.appInstance.check(): message = "pidfile %s already exist. Daemon already running?\n" logger.error(message % self.pidfile, self) sys.exit(1) # Start the daemon self.daemonize() self.run() def stop(self): """ Stop the daemon """ # Get the pid from the pidfile pid, procname = self.appInstance.readPidFile() if not pid: message = "pidfile %s does not exist. Daemon not running?\n" logger.error(message % self.pidfile, self) return # not an error in a restart # Try killing the daemon process try: while True: os.kill(pid, signal.SIGTERM) time.sleep(0.1) except OSError as err: if err.errno == errno.ESRCH: #no such process self.appInstance.exit_application() else: logger.error(str(err), self) sys.exit(1) def restart(self): """ Restart the daemon """ self.stop() self.start() def reload(self): """ send SIGHUP signal to process """ # Get the pid from the pidfile pid, procname = self.appInstance.readPidFile() if not pid: message = "pidfile %s does not exist. Daemon not running?\n" logger.error(message % self.pidfile, self) return # Try killing the daemon process try: os.kill(pid, signal.SIGHUP) except OSError as err: if err.errno == errno.ESRCH: #no such process self.appInstance.exit_application() else: sys.stderr.write(str(err)) sys.exit(1) def status(self): """ return status """ return not self.appInstance.check() def run(self): """ You should override this method when you subclass Daemon. It will be called after the process has been daemonized by start() or restart(). """ pass
class TestApplicationInstance(unittest.TestCase): def setUp(self): logger.DEBUG = '-v' in sys.argv self.temp_file = '/tmp/temp.txt' self.file_name = "/tmp/file_with_pid" self.inst = ApplicationInstance(os.path.abspath(self.file_name), False) self.subproc = None def tearDown(self): for f in (self.temp_file, self.file_name): if os.path.exists(f): os.remove(f) if self.subproc: self.subproc.kill() self.subproc = None def createProcess(self): with open(self.temp_file, 'wt') as output: self.subproc = subprocess.Popen("top", stdout=output) return self.subproc.pid def getProcName(self, pid): with open('/proc/%s/cmdline' % pid, 'r') as file: return file.read().strip('\n') def test_create_and_remove_pid_file(self): #create pid file self.inst.start_application() self.assertTrue(os.path.isfile(self.file_name)) #remove pid file self.inst.exit_application() self.assertFalse(os.path.isfile(self.file_name)) def test_write_pid_file(self): self.inst.start_application() #get pid/procname of current process this_pid = os.getpid() this_procname = self.getProcName(this_pid) with open(self.file_name, 'rt') as file_with_pid: self.assertEqual(file_with_pid.read(), '%s\n%s' %(this_pid, this_procname)) def test_existing_process_with_correct_procname(self): """ Test the check function with an existing process with correct process name """ pid = self.createProcess() procname = self.getProcName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) # Execute test self.assertFalse(self.inst.check()) def test_existing_process_with_wrong_procname(self): """ Test the check function with an existing process with wrong process name """ pid = self.createProcess() procname = self.getProcName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname + "DELETE") # Execute test self.assertTrue(self.inst.check()) def test_existing_process_with_wrong_pid(self): """ Test the check function with an existing process with wrong pid """ pid = self.createProcess() procname = self.getProcName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write("987654321\n") file_with_pid.write(procname) # Execute test self.assertTrue(self.inst.check()) def test_killing_existing_process(self): """ Test the check function with an existing process with correct process name """ pid = self.createProcess() procname = self.getProcName(pid) # create file with pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write(str(pid) + "\n") file_with_pid.write(procname) #kill process self.subproc.kill() self.subproc.wait() self.subproc = None # Execute test self.assertTrue(self.inst.check()) def test_non_existing_process(self): """ Test the check function with a non existing process """ # GIVE # # create file with fake pid and process name with open(self.file_name, "wt") as file_with_pid: file_with_pid.write("987654321\n") file_with_pid.write("FAKE_PROCNAME") # Execute test self.assertTrue(self.inst.check()) def test_leftover_empty_lockfile(self): with open(self.file_name, 'wt')as f: pass self.assertTrue(self.inst.check()) def write_after_flock(self, pid_file,): inst = ApplicationInstance(os.path.abspath(pid_file), False) inst.flockExclusiv() with open(self.temp_file, 'wt') as f: f.write('foo') inst.flockUnlock() def test_thread_write_without_flock(self): thread = Thread(target = self.write_after_flock, args = (self.file_name, )) thread.start() #wait for the thread to finish thread.join() self.assertTrue(os.path.exists(self.temp_file)) with open(self.temp_file, 'rt') as f: self.assertEqual(f.read(), 'foo') def test_flock_exclusive(self): self.inst.flockExclusiv() thread = Thread(target = self.write_after_flock, args = (self.file_name, )) thread.start() #give the thread some time sleep(0.01) self.assertFalse(os.path.exists(self.temp_file)) self.inst.flockUnlock() #wait for the thread to finish thread.join() self.assertTrue(os.path.exists(self.temp_file)) with open(self.temp_file, 'rt') as f: self.assertEqual(f.read(), 'foo')