From: Frank Brehm Date: Mon, 4 Jul 2011 13:26:43 +0000 (+0000) Subject: Stand der Perl-Anwendung erreicht, es fehlen noch die Mailerei und Übersetzungen X-Git-Url: https://git.uhu-banane.de/?a=commitdiff_plain;h=8451249e41cb37221059363f8927fcea5385b43f;p=my-stuff%2Fpy-logrotate.git Stand der Perl-Anwendung erreicht, es fehlen noch die Mailerei und Übersetzungen git-svn-id: http://svn.brehm-online.com/svn/my-stuff/python/PyLogrotate/trunk@274 ec8d2aa5-1599-4edb-8739-2b3a1bc399aa --- diff --git a/LogRotateConfig.py b/LogRotateConfig.py index add1806..cb35c04 100755 --- a/LogRotateConfig.py +++ b/LogRotateConfig.py @@ -24,6 +24,7 @@ import grp import glob from LogRotateCommon import split_parts, email_valid, period2days, human2bytes +from LogRotateScript import LogRotateScript revision = '$Revision$' revision = re.sub( r'\$', '', revision ) @@ -154,9 +155,10 @@ class LogrotateConfigurationReader(object): #------------------------------------------------------- def __init__( self, config_file, - verbose = 0, - logger = None, - local_dir = None, + verbose = 0, + logger = None, + local_dir = None, + test_mode = False, ): ''' Constructor. @@ -171,6 +173,8 @@ class LogrotateConfigurationReader(object): are located. If None, then system default (/usr/share/locale) is used. @type local_dir: str or None + @param test_mode: test mode - no write actions are made + @type test_mode: bool @return: None ''' @@ -205,6 +209,12 @@ class LogrotateConfigurationReader(object): @type: str ''' + self.test_mode = test_mode + ''' + @ivar: test mode - no write actions are made + @type: bool + ''' + self.logger = logger.getChild('config') ''' @ivar: logger object @@ -324,8 +334,9 @@ class LogrotateConfigurationReader(object): self.scripts = {} ''' - @ivar: list of all named scripts found in configuration - @type: list + @ivar: dict of LogRotateScript objects + with all named scripts found in configuration + @type: dict ''' self.defined_logfiles = {} @@ -371,13 +382,17 @@ class LogrotateConfigurationReader(object): 'local_dir': self.local_dir, 'new_log': self.new_log, 'search_path': self.search_path, - 'scripts': self.scripts, + 'scripts': {}, 'shred_command': self.shred_command, 't': self.t, 'taboo': self.taboo, + 'test_mode': self.test_mode, 'verbose': self.verbose, } + for script_name in self.scripts.keys(): + res['scripts'][script_name] = self.scripts[script_name].as_dict() + return res #------------------------------------------------------------ @@ -737,7 +752,8 @@ class LogrotateConfigurationReader(object): if match: in_script = False continue - self.scripts[newscript]['cmd'].append(line) + #self.scripts[newscript]['cmd'].append(line) + self.scripts[newscript].add_cmd(line) continue # start of a logfile definition @@ -844,6 +860,20 @@ class LogrotateConfigurationReader(object): if self.verbose > 3: self.logger.debug( ( _("New logfile definition:") + "\n" + pp.pformat(self.new_log))) if found_files > 0: + if self.new_log['postrotate']: + script = self.new_log['postrotate'] + if self.scripts[script]: + self.scripts[script].post_files += found_files + else: + msg = _("Postrotate script '%s' not found.") % (script) + self.logger.error(msg) + if self.new_log['lastaction']: + script = self.new_log['lastaction'] + if self.scripts[script]: + self.scripts[script].last_files += found_files + else: + msg = _("Lastaction script '%s' not found.") % (script) + self.logger.error(msg) self.config.append(self.new_log) in_fd = False in_logfile_list = False @@ -1583,14 +1613,19 @@ class LogrotateConfigurationReader(object): % {'name': script_name, 'file': filename, 'lnr': linenr}) ) - self.scripts[script_name] = {} - self.scripts[script_name]['cmd'] = [] - self.scripts[script_name]['post'] = False - self.scripts[script_name]['last'] = False - self.scripts[script_name]['first'] = False - self.scripts[script_name]['prerun'] = False - self.scripts[script_name]['donepost'] = False - self.scripts[script_name]['donelast'] = False + self.scripts[script_name] = LogRotateScript( + name = script_name, + local_dir = self.local_dir, + verbose = self.verbose, + test_mode = self.test_mode, + ) + #self.scripts[script_name]['cmd'] = [] + #self.scripts[script_name]['post_files'] = 0 + #self.scripts[script_name]['last_files'] = 0 + #self.scripts[script_name]['first'] = False + #self.scripts[script_name]['prerun'] = False + #self.scripts[script_name]['donepost'] = False + #self.scripts[script_name]['donelast'] = False return script_name @@ -1797,14 +1832,20 @@ class LogrotateConfigurationReader(object): new_script_name = self._new_scriptname(script_type) - self.scripts[new_script_name] = {} - self.scripts[new_script_name]['cmd'] = [] - self.scripts[new_script_name]['post'] = False - self.scripts[new_script_name]['last'] = False - self.scripts[new_script_name]['first'] = False - self.scripts[new_script_name]['prerun'] = False - self.scripts[new_script_name]['donepost'] = False - self.scripts[new_script_name]['donelast'] = False + self.scripts[new_script_name] = LogRotateScript( + name = new_script_name, + local_dir = self.local_dir, + verbose = self.verbose, + test_mode = self.test_mode, + ) + #self.scripts[new_script_name] = {} + #self.scripts[new_script_name]['cmd'] = [] + #self.scripts[new_script_name]['post_files'] = 0 + #self.scripts[new_script_name]['last_files'] = 0 + #self.scripts[new_script_name]['first'] = False + #self.scripts[new_script_name]['prerun'] = False + #self.scripts[new_script_name]['donepost'] = False + #self.scripts[new_script_name]['donelast'] = False self.new_log[script_type] = new_script_name @@ -1830,7 +1871,7 @@ class LogrotateConfigurationReader(object): if name in self.scripts: if 'cmd' in self.scripts[name]: - if len(self.scripts[name]['cmd']): + if len(self.scripts[name].cmd): i += 1 name = template % (i) else: diff --git a/LogRotateHandler.py b/LogRotateHandler.py index 23398a4..ce7780d 100755 --- a/LogRotateHandler.py +++ b/LogRotateHandler.py @@ -9,7 +9,7 @@ @contact: frank@brehm-online.com @license: GPL3 @copyright: (c) 2010-2011 by Frank Brehm, Berlin -@version: 0.1.0 +@version: 0.4.0 @summary: Application handler module for Python logrotating ''' @@ -48,7 +48,7 @@ revision = re.sub( r'\s*$', '', revision ) __author__ = 'Frank Brehm' __copyright__ = '(C) 2011 by Frank Brehm, Berlin' __contact__ = 'frank@brehm-online.com' -__version__ = '0.3.0 ' + revision +__version__ = '0.4.0 ' + revision __license__ = 'GPL3' @@ -214,7 +214,7 @@ class LogrotateHandler(object): self.scripts = {} ''' - @ivar: list of all named scripts found in configuration + @ivar: list of LogRotateScript objects with all named scripts found in configuration @type: list ''' @@ -352,7 +352,7 @@ class LogrotateHandler(object): 'logfiles': self.logfiles, 'logger': self.logger, 'mail_cmd': self.mail_cmd, - 'scripts': self.scripts, + 'scripts': {}, 'state_file': None, 'state_file_name': self.state_file_name, 'pid_file': self.pid_file, @@ -365,6 +365,9 @@ class LogrotateHandler(object): if self.state_file: res['state_file'] = self.state_file.as_dict() + for script_name in self.scripts.keys(): + res['scripts'][script_name] = self.scripts[script_name].as_dict() + return res #------------------------------------------------------------ @@ -426,6 +429,7 @@ class LogrotateHandler(object): verbose = self.verbose, logger = self.logger, local_dir = self.local_dir, + test_mode = self.test, ) if self.verbose > 2: @@ -586,6 +590,14 @@ class LogrotateHandler(object): self._rotate_definition(cur_desc_index) cur_desc_index += 1 + # Check for left over scripts to execute + for scriptname in self.scripts.keys(): + if self.verbose >= 4: + msg = ( _("State of script '%s':") % (scriptname) ) \ + + "\n" + str(self.scripts[scriptname]) + self.logger.debug(msg) + del self.scripts[scriptname] + return #------------------------------------------------------------ @@ -662,17 +674,30 @@ class LogrotateHandler(object): if self.verbose > 2: msg = _("Looking, whether the firstaction script should be executed.") self.logger.debug(msg) - if not self.scripts[firstscript]['first']: + if not self.scripts[firstscript].done_firstrun: msg = _("Executing firstaction script '%s' ...") % (firstscript) self.logger.info(msg) - if not self.test: - cmd = '\n'.join(self.scripts[firstscript]['cmd']) - if not self._execute_command(cmd): - return - self.scripts[firstscript]['first'] = True + if not self.scripts[firstscript].execute(): + return + self.scripts[firstscript].done_firstrun = True - # Executing prerotate scripts ... - # bla bla bla + # Executing prerotate scripts, if not sharedscripts or even not executed + if prescript: + if self.verbose > 2: + msg = _("Looking, whether the prerun script should be executed.") + self.logger.debug(msg) + do_it = False + if sharedscripts: + if not self.scripts[prescript].done_prerun: + do_it = True + else: + do_it = True + if do_it: + msg = _("Executing prerun script '%s' ...") % (prescript) + self.logger.info(msg) + if not self.scripts[prescript].execute(): + return + self.scripts[prescript].done_prerun = True olddir = self._create_olddir(logfile, cur_desc_index) if olddir is None: @@ -681,6 +706,48 @@ class LogrotateHandler(object): if not self._do_rotate_file(logfile, cur_desc_index, olddir): return + # Looking for postrotate script in a similar way like for the prerotate + if postscript: + if self.verbose > 2: + msg = _("Looking, whether the postrun script should be executed.") + self.logger.debug(msg) + do_it = False + self.scripts[postscript].post_files -= 1 + self.scripts[postscript].do_post = True + if sharedscripts: + if self.scripts[postscript].post_files <= 0: + do_it = True + self.scripts[postscript].do_post = False + else: + do_it = True + if do_it: + msg = _("Executing postrun script '%s' ...") % (postscript) + self.logger.info(msg) + if not self.scripts[prescript].execute(): + return + self.scripts[postscript].done_postrun = True + + # Looking for lastaction script + if lastscript: + if self.verbose > 2: + msg = _("Looking, whether the lastaction script should be executed.") + self.logger.debug(msg) + do_it = False + self.scripts[lastscript].last_files -= 1 + self.scripts[lastscript].do_last = True + if self.scripts[lastscript].done_lastrun: + self.scripts[lastscript].do_last = False + else: + if self.scripts[lastscript].last_files <= 0: + do_it = True + self.scripts[lastscript].do_last = False + if do_it: + msg = _("Executing lastaction script '%s' ...") % (lastscript) + self.logger.info(msg) + if not self.scripts[lastscript].execute(): + return + self.scripts[lastscript].done_lastrun = True + #------------------------------------------------------------ def _do_rotate_file(self, logfile, cur_desc_index, olddir = None): ''' diff --git a/LogRotateScript.py b/LogRotateScript.py new file mode 100755 index 0000000..aac549c --- /dev/null +++ b/LogRotateScript.py @@ -0,0 +1,529 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# $Id$ +# $URL$ + +''' +@author: Frank Brehm +@contact: frank@brehm-online.com +@license: GPL3 +@copyright: (c) 2010-2011 by Frank Brehm, Berlin +@version: 0.0.2 +@summary: module for a logrotate script object + (for pre- and postrotate actions) +''' +import re +import logging +import subprocess +import pprint +import gettext + +revision = '$Revision$' +revision = re.sub( r'\$', '', revision ) +revision = re.sub( r'Revision: ', r'r', revision ) +revision = re.sub( r'\s*$', '', revision ) + +__author__ = 'Frank Brehm' +__copyright__ = '(C) 2011 by Frank Brehm, Berlin' +__contact__ = 'frank@brehm-online.com' +__version__ = '0.1.0 ' + revision +__license__ = 'GPL3' + +#======================================================================== + +class LogRotateScriptError(Exception): + ''' + Base class for exceptions in this module. + ''' + +#======================================================================== + +class LogRotateScript(object): + ''' + Class for encapsulating a logrotate script + (for pre- and postrotate actions) + + @author: Frank Brehm + @contact: frank@brehm-online.com + ''' + + #------------------------------------------------------- + def __init__( self, name, + local_dir = None, + verbose = 0, + test_mode = False, + ): + ''' + Constructor. + + @param name: the name of the script as an identifier + @type name: str + @param local_dir: The directory, where the i18n-files (*.mo) + are located. If None, then system default + (/usr/share/locale) is used. + @type local_dir: str or None + @param verbose: verbosity (debug) level + @type verbose: int + @param test_mode: test mode - no write actions are made + @type test_mode: bool + @param logger: logger object to use for logging a.s.o. + @type logger: logging.getLogger or None + + @return: None + ''' + + self.t = gettext.translation( + 'LogRotateScript', + local_dir, + fallback = True + ) + ''' + @ivar: a gettext translation object + @type: gettext.translation + ''' + + _ = self.t.lgettext + + self.verbose = verbose + ''' + @ivar: verbosity level (0 - 9) + @type: int + ''' + + self._name = name + ''' + @ivar: the name of the script as an identifier + @type: str + ''' + + self.test_mode = test_mode + ''' + @ivar: test mode - no write actions are made + @type: bool + ''' + + self.logger = logging.getLogger('pylogrotate.script') + ''' + @ivar: logger object + @type: logging.getLogger + ''' + + self._cmd = [] + ''' + @ivar: List of commands to execute + @type: list + ''' + + self._post_files = 0 + ''' + @ivar: Number of logfiles referencing to this script + as a postrotate script + @type: int + ''' + + self._last_files = 0 + ''' + @ivar: Number of logfiles referencing to this script + as a lastaction script + @type: int + ''' + + self._done_firstrun = False + ''' + @ivar: Flag, whether the script was executed as + a firstaction script + @type: bool + ''' + + self._done_prerun = False + ''' + @ivar: Flag, whether the script was executed as + a prerun script + @type: bool + ''' + + self._done_postrun = False + ''' + @ivar: Flag, whether the script was executed as + a postrun script + @type: bool + ''' + + self._done_lastrun = False + ''' + @ivar: Flag, whether the script was executed as + a lastaction script + @type: bool + ''' + + self._do_post = False + ''' + Runtime flag, that the script should be executed + as an postrun script + ''' + + self._do_last = False + ''' + Runtime flag, that the script should be executed + as an lastaction script + ''' + + #------------------------------------------------------------ + # Defintion of some properties + + #------------------------------------------------------------ + @property + def name(self): + ''' + Property 'name' as the name of the script as an identifier + + readonly + ''' + return self._name + + #------------------------------------------------------------ + @property + def cmd(self): + ''' + Property 'cmd' as the commands to execute + ''' + if len(self._cmd): + return "\n".join(self._cmd) + else: + return None + + #--------------- + @cmd.setter + def cmd(self, value): + ''' + Setter of property 'cmd' + + @param value: the command to set to self._cmd + @type value: str + + @return: None + ''' + if value: + if isinstance(value, list): + self._cmd = value[:] + else: + self._cmd = [value] + else: + self._cmd = [] + + #--------------- + @cmd.deleter + def cmd(self): + ''' + Deleter of property 'cmd' + ''' + self._cmd = [] + + #------------------------------------------------------------ + @property + def post_files(self): + ''' + Property 'post_files' as the number of logfiles + referencing to this script as a postrotate script + ''' + return self._post_files + + #--------------- + @post_files.setter + def post_files(self, value): + ''' + Setter of property 'post_files' + ''' + + _ = self.t.lgettext + if isinstance(value, int): + self._post_files = value + else: + msg = _("Invalid value for property '%s' given.") % ('post_files') + raise LogRotateScriptError(msg) + + #------------------------------------------------------------ + @property + def last_files(self): + ''' + Property 'last_files' as the number of logfiles + referencing to this script as a lastaction script + ''' + return self._last_files + + #--------------- + @last_files.setter + def last_files(self, value): + ''' + Setter of property 'last_files' + ''' + + _ = self.t.lgettext + if isinstance(value, int): + self._last_files = value + else: + msg = _("Invalid value for property '%s' given.") % ('last_files') + raise LogRotateScriptError(msg) + + #------------------------------------------------------------ + @property + def done_firstrun(self): + ''' + Property 'done_firstrun' as a flag, whether the script + was executed as a firstaction script + ''' + return self._done_firstrun + + #--------------- + @done_firstrun.setter + def done_firstrun(self, value): + ''' + Setter of property 'done_firstrun' + ''' + self._done_firstrun = bool(value) + + #------------------------------------------------------------ + @property + def done_prerun(self): + ''' + Property 'done_prerun' as a flag, whether the script + was executed as a prerun script + ''' + return self._done_prerun + + #--------------- + @done_prerun.setter + def done_prerun(self, value): + ''' + Setter of property 'done_prerun' + ''' + self._done_prerun = bool(value) + + #------------------------------------------------------------ + @property + def done_postrun(self): + ''' + Property 'done_postrun' as a flag, whether the script + was executed as a postrun script + ''' + return self._done_postrun + + #--------------- + @done_postrun.setter + def done_postrun(self, value): + ''' + Setter of property 'done_postrun' + ''' + self._done_postrun = bool(value) + + #------------------------------------------------------------ + @property + def done_lastrun(self): + ''' + Property 'done_lastrun' as a flag, whether the script + was executed as a lastaction script + ''' + return self._done_lastrun + + #--------------- + @done_lastrun.setter + def done_lastrun(self, value): + ''' + Setter of property 'done_lastrun' + ''' + self._done_lastrun = bool(value) + + #------------------------------------------------------------ + @property + def do_post(self): + ''' + Property 'do_post' as a flag, whether the script + should be executed as a postrun script + ''' + return self._do_post + + #--------------- + @do_post.setter + def do_post(self, value): + ''' + Setter of property 'do_post' + ''' + self._do_post = bool(value) + + #------------------------------------------------------------ + @property + def do_last(self): + ''' + Property 'do_last' as a flag, whether the script + should be executed as a lastaction script + ''' + return self._do_last + + #--------------- + @do_last.setter + def do_last(self, value): + ''' + Setter of property 'do_last' + ''' + self._do_last = bool(value) + + #------------------------------------------------------------ + # Other Methods + + #------------------------------------------------------- + def __del__(self): + ''' + Destructor. + Checks, whether the script should even be run as + a postrun or a lastaction script + ''' + + _ = self.t.lgettext + if self.verbose > 2: + msg = _("Logrotate script object '%s' will destroyed.") % (self.name) + self.logger.debug(msg) + + self.check_for_execute() + + #------------------------------------------------------------ + def __str__(self): + ''' + Typecasting function for translating object structure + into a string + + @return: structure as string + @rtype: str + ''' + + pp = pprint.PrettyPrinter(indent=4) + structure = self.as_dict() + return pp.pformat(structure) + + #------------------------------------------------------- + def as_dict(self): + ''' + Transforms the elements of the object into a dict + + @return: structure as dict + @rtype: dict + ''' + + res = {} + res['t'] = self.t + res['verbose'] = self.verbose + res['name'] = self.name + res['test_mode'] = self.test_mode + res['logger'] = self.logger + res['cmd'] = self._cmd[:] + res['post_files'] = self.post_files + res['last_files'] = self.last_files + res['done_firstrun'] = self.done_firstrun + res['done_prerun'] = self.done_prerun + res['done_postrun'] = self.done_postrun + res['done_lastrun'] = self.done_lastrun + res['do_post'] = self.do_post + res['do_last'] = self.do_last + + return res + + #------------------------------------------------------------ + def add_cmd(self, cmd): + ''' + Adding a command to the list self._cmd + + @param cmd: the command to add to self._cmd + @type cmd: str + + @return: None + ''' + self._cmd.append(cmd) + + #------------------------------------------------------------ + def execute(self, force=False, expected_retcode=0): + ''' + Executes the command as an OS command in a shell. + + @param force: force executing command even + if self.test_mode == True + @type force: bool + @param expected_retcode: expected returncode of the command + (should be 0) + @type expected_retcode: int + + @return: Success of the comand (shell returncode == 0) + @rtype: bool + ''' + + _ = self.t.lgettext + cmd = self.cmd + if cmd is None: + msg = _("No command to execute defined in script '%s'.") % (self.name) + raise LogRotateScriptError(msg) + return False + if self.verbose > 3: + msg = _("Executing script '%(name)s' with command: '%(cmd)s'") \ + % {'name': self.name, 'cmd': cmd} + self.logger.debug(msg) + if not force: + if self.test_mode: + return True + try: + retcode = subprocess.call(command, shell=True) + if self.verbose > 3: + msg = _("Got returncode for script '%(name)s': '%(retcode)s'") \ + % {'name': self.name, 'retcode': retcode} + self.logger.debug(msg) + if retcode < 0: + msg = _("Child in script '%(name)s' was terminated by signal %(retcode)d") \ + % {'name': self.name, 'retcode': -retcode} + self.logger.error(msg) + return False + if retcode != expected_retcode: + return False + return True + except OSError, e: + msg = _("Execution of script '%(name)s' failed: %(error)s") \ + % {'name': self.name, 'error': str(e)} + self.logger.error(msg) + return False + + return False + + #------------------------------------------------------------ + def check_for_execute(self, force=False, expected_retcode=0): + ''' + Checks, whether the script should executed. + + @param force: force executing command even + if self.test_mode == True + @type force: bool + @param expected_retcode: expected returncode of the command + (should be 0) + @type expected_retcode: int + + @return: Success of execution + @rtype: bool + ''' + + _ = self.t.lgettext + msg = _("Checking, whether the script '%s' should be executed.") % (self.name) + self.logger.debug(msg) + + if self.do_post or self.do_last: + result = self.execute(force=force, expected_retcode=expected_retcode) + self.do_post = False + self.do_last = False + return result + + return True + +#======================================================================== + +if __name__ == "__main__": + pass + + +#======================================================================== + +# vim: fileencoding=utf-8 filetype=python ts=4 expandtab diff --git a/po/LogRotateConfig.de.po b/po/LogRotateConfig.de.po index aa314c8..9feb716 100644 --- a/po/LogRotateConfig.de.po +++ b/po/LogRotateConfig.de.po @@ -144,15 +144,25 @@ msgstr "" "Keine Dateierweiterung für die komprimierten Logdateien gegeben " "(Beginn der Definition: Datei »%(file)s«, Zeile %(rownum)d)." -#: LogRotateConfig.py:791 +#: LogRotateConfig.py:845 msgid "New logfile definition:" msgstr "Neue Logdateidefinition:" -#: LogRotateConfig.py:804 +#: LogRotateConfig.py:852 +#, python-format +msgid "Postrotate script '%s' not found." +msgstr "Das Postrotate-Skript »%s« wurde nicht gefunden." + +#: LogRotateConfig.py:859 +#, python-format +msgid "Lastaction script '%s' not found." +msgstr "Das Lastaction-Skript »%s« wurde nicht gefunden." + +#: LogRotateConfig.py:873 #, python-format msgid "" -"Syntax error: include may not appear inside of log file definition (file " -"'%(file)s', line %(line)s)" +"Syntax error: include may not appear inside of log file definition " +"(file '%(file)s', line %(line)s)" msgstr "" "Syntaxfehler: Eine include-Anweisung darf nicht einer Logdateidefinition auftreten " "(Datei »%(file)s«, Zeile %(line)s)" diff --git a/po/LogRotateConfig.pot b/po/LogRotateConfig.pot index b962b87..2b7744f 100644 --- a/po/LogRotateConfig.pot +++ b/po/LogRotateConfig.pot @@ -130,15 +130,25 @@ msgid "" "(File of definition: '%(file)s', start definition: %(rownum)d)." msgstr "" -#: LogRotateConfig.py:791 +#: LogRotateConfig.py:845 msgid "New logfile definition:" msgstr "" -#: LogRotateConfig.py:804 +#: LogRotateConfig.py:852 +#, python-format +msgid "Postrotate script '%s' not found." +msgstr "" + +#: LogRotateConfig.py:859 +#, python-format +msgid "Lastaction script '%s' not found." +msgstr "" + +#: LogRotateConfig.py:873 #, python-format msgid "" -"Syntax error: include may not appear inside of log file definition (file '%(file)s', line " -"%(line)s)" +"Syntax error: include may not appear inside of log file definition " +"(file '%(file)s', line %(line)s)" msgstr "" #: LogRotateConfig.py:823