From 9dcab958bdaadb2787027b9b10af362a0bc2f861 Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Wed, 14 Oct 2009 16:47:31 +0300 Subject: skytools.DBScript: safer pidfile writing - signal_pidfile: Clarify ValueError error message. Thrown usually on empty pidfiles, the error messega will now mention the pidfile name. - run_single_process: restructure pidfile writing, so that the pidfile is removed if .write() failed, but not when open() failed. This should avoid the chance that empty pidfiles are hanging around. --- python/skytools/scripting.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'python/skytools/scripting.py') diff --git a/python/skytools/scripting.py b/python/skytools/scripting.py index 83e050eb..0d4b0fab 100644 --- a/python/skytools/scripting.py +++ b/python/skytools/scripting.py @@ -42,7 +42,7 @@ def signal_pidfile(pidfile, sig): """Send a signal to process whose ID is located in pidfile. Read only first line of pidfile to support multiline - pifiles like postmaster.pid. + pidfiles like postmaster.pid. Returns True is successful, False if pidfile does not exist or process itself is dead. Any other errors will passed @@ -58,6 +58,8 @@ def signal_pidfile(pidfile, sig): except OSError, ex: if ex.errno != errno.ESRCH: raise + except ValueError, ex: + raise ValueError('Corrupt pidfile: %s' % pidfile) return False # @@ -90,12 +92,6 @@ def daemonize(): # Pidfile locking+cleanup & daemonization combined # -def _write_pidfile(pidfile): - pid = os.getpid() - f = open(pidfile, 'w') - f.write(str(pid)) - f.close() - def run_single_process(runnable, daemon, pidfile): """Run runnable class, possibly daemonized, locked on pidfile.""" @@ -107,17 +103,23 @@ def run_single_process(runnable, daemon, pidfile): else: print("Ignoring stale pidfile") - # daemonize if needed and write pidfile + # daemonize if needed if daemon: daemonize() - if pidfile: - _write_pidfile(pidfile) - - # run and clean pidfile later + + # clean only own pidfile + own_pidfile = False + try: + if pidfile: + f = open(pidfile, 'w') + own_pidfile = True + f.write(str(os.getpid())) + f.close() + runnable.run() finally: - if pidfile: + if own_pidfile: try: os.remove(pidfile) except: pass -- cgit v1.2.3 From 15042c82b01ac74385068c70c7ccf84e1407186d Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Wed, 14 Oct 2009 18:03:54 +0300 Subject: python/: Add --version switch to all scripts. Based on patch by Hannu Krosing --- Makefile | 2 +- config.mak.in | 5 +++++ python/skytools/__init__.py | 3 +++ python/skytools/installer_config.py.in | 3 +++ python/skytools/scripting.py | 8 ++++++++ 5 files changed, 20 insertions(+), 1 deletion(-) (limited to 'python/skytools/scripting.py') diff --git a/Makefile b/Makefile index 976597d3..8a5df5c8 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ python-install: config.mak sub-all python-install python-all: python/skytools/installer_config.py python/skytools/installer_config.py: python/skytools/installer_config.py.in config.mak - sed -e 's!@SQLDIR@!$(SQLDIR)!g' $< > $@ + sed -e 's!@SQLDIR@!$(SQLDIR)!g' -e 's!@PACKAGE_VERSION@!$(PACKAGE_VERSION)!g' $< > $@ realclean: distclean $(MAKE) -C doc $@ diff --git a/config.mak.in b/config.mak.in index 894c8c98..d105f635 100644 --- a/config.mak.in +++ b/config.mak.in @@ -36,3 +36,8 @@ INSTALL = @INSTALL@ BININSTALL = @BININSTALL@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_STRING = @PACKAGE_STRING@ + diff --git a/python/skytools/__init__.py b/python/skytools/__init__.py index b958b483..03b18a04 100644 --- a/python/skytools/__init__.py +++ b/python/skytools/__init__.py @@ -35,3 +35,6 @@ __all__ = (skytools.psycopgwrapper.__all__ + skytools.adminscript.__all__ + skytools.parsing.__all__) +import skytools.installer_config +__version__ = skytools.installer_config.package_version + diff --git a/python/skytools/installer_config.py.in b/python/skytools/installer_config.py.in index a01621f7..2c684c17 100644 --- a/python/skytools/installer_config.py.in +++ b/python/skytools/installer_config.py.in @@ -6,3 +6,6 @@ __all__ = ['sql_locations'] sql_locations = [ "@SQLDIR@", ] + +package_version = "@PACKAGE_VERSION@" + diff --git a/python/skytools/scripting.py b/python/skytools/scripting.py index 0d4b0fab..57401773 100644 --- a/python/skytools/scripting.py +++ b/python/skytools/scripting.py @@ -325,6 +325,9 @@ class DBScript(object): self.options, self.args = parser.parse_args(args) # check args + if self.options.version: + self.print_version() + sys.exit(0) if self.options.daemon: self.go_daemon = 1 if self.options.quiet: @@ -353,6 +356,9 @@ class DBScript(object): elif self.options.cmd == "reload": self.send_signal(signal.SIGHUP) + def print_version(self): + print '%s, Skytools version %s' % (self.service_name, skytools.__version__) + def print_ini(self): """Prints out ini file from doc string of the script of default for dbscript @@ -431,6 +437,8 @@ class DBScript(object): help = "log verbosely") p.add_option("-d", "--daemon", action="store_true", help = "go background") + p.add_option("-V", "--version", action="store_true", + help = "print version info and exit") p.add_option("", "--ini", action="store_true", help = "display sample ini file") -- cgit v1.2.3 From 2293f7843ebf9a5222edf4d980785983c72187b1 Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Tue, 3 Nov 2009 14:28:27 +0200 Subject: DBScript: use log.exception also for psycopg errors --- python/skytools/scripting.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'python/skytools/scripting.py') diff --git a/python/skytools/scripting.py b/python/skytools/scripting.py index 57401773..49be0fcc 100644 --- a/python/skytools/scripting.py +++ b/python/skytools/scripting.py @@ -643,9 +643,11 @@ class DBScript(object): cname = d.cursor.connection.my_name dsn = d.cursor.connection.dsn sql = d.cursor.query + if len(sql) > 200: # avoid logging londiste huge batched queries + sql = sql[:60] + " ..." emsg = str(d).strip() - self.log.error("Job %s got error on connection '%s': %s" % ( - self.job_name, cname, emsg)) + self.log.exception("Job %s got error on connection '%s': %s. Query: %s" % ( + self.job_name, cname, emsg, sql)) else: n = "psycopg2.%s" % d.__class__.__name__ emsg = str(d).rstrip() -- cgit v1.2.3 From 5951e75aca8bdadb5433ebbd1e1c551d88983e59 Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Wed, 4 Nov 2009 14:24:28 +0200 Subject: DBScript: avoid second reload on startup --- python/skytools/scripting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'python/skytools/scripting.py') diff --git a/python/skytools/scripting.py b/python/skytools/scripting.py index 49be0fcc..4d184fce 100644 --- a/python/skytools/scripting.py +++ b/python/skytools/scripting.py @@ -316,7 +316,7 @@ class DBScript(object): self.service_name = service_name self.db_cache = {} self.go_daemon = 0 - self.need_reload = 1 + self.need_reload = 0 self.stat_dict = {} self.log_level = logging.INFO -- cgit v1.2.3 From 96f699c1002a4b7cd050f5ebfe0abee24913c9b8 Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Wed, 4 Nov 2009 15:28:49 +0200 Subject: DBScript: restore .looping variable loop_delay is loaded from conf, so it conflicts with .set_single_loop() in scripts which have both looping mode and non-looping mode. Eg: londiste, walmgr --- python/skytools/scripting.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'python/skytools/scripting.py') diff --git a/python/skytools/scripting.py b/python/skytools/scripting.py index 4d184fce..9d7c2b17 100644 --- a/python/skytools/scripting.py +++ b/python/skytools/scripting.py @@ -292,8 +292,16 @@ class DBScript(object): cf = None log = None pidfile = None + + # >0 - sleep time if work() requests sleep + # 0 - exit if work requests sleep + # <0 - run work() once [same as looping=0] loop_delay = 0 + # 0 - run work() once + # 1 - run work() repeatedly + looping = 1 + # result from last work() call: # 1 - there is probably more work, don't sleep # 0 - no work, sleep before calling again @@ -342,7 +350,6 @@ class DBScript(object): sys.exit(1) # read config file - self.cf = self.load_config() self.reload() # init logging @@ -471,7 +478,9 @@ class DBScript(object): def set_single_loop(self, do_single_loop): """Changes whether the script will loop or not.""" if do_single_loop: - self.loop_delay = 0 + self.looping = 0 + else: + self.looping = 1 def _boot_daemon(self): run_single_process(self, self.go_daemon, self.pidfile) @@ -486,7 +495,7 @@ class DBScript(object): def stop(self): """Safely stops processing loop.""" - self.loop_delay = 0 + self.looping = 0 def reload(self): "Reload config." @@ -604,11 +613,14 @@ class DBScript(object): for dbc in self.db_cache.values(): dbc.refresh() + if not self.looping or self.loop_delay < 0: + break + # remember work state self.work_state = work # should sleep? if not work: - if self.loop_delay: + if self.loop_delay > 0: time.sleep(self.loop_delay) else: break @@ -620,6 +632,9 @@ class DBScript(object): "Run users work function, safely." cname = None emsg = None + if prefer_looping: + if not self.looping or self.loop_delay <= 0: + prefer_looping = False try: return func() except UsageError, d: @@ -627,13 +642,13 @@ class DBScript(object): sys.exit(1) except SystemExit, d: self.send_stats() - if prefer_looping and self.loop_delay: + if prefer_looping: self.log.info("got SystemExit(%s), exiting" % str(d)) self.reset() raise d except KeyboardInterrupt, d: self.send_stats() - if prefer_looping and self.loop_delay: + if prefer_looping: self.log.info("got KeyboardInterrupt, exiting") self.reset() sys.exit(1) @@ -662,7 +677,7 @@ class DBScript(object): # reset and sleep self.reset() self.exception_hook(d, emsg, cname) - if prefer_looping and self.loop_delay: + if prefer_looping: time.sleep(20) return -1 sys.exit(1) -- cgit v1.2.3