From 1a99f9bb9adabf7fa576dd83807bab2ec533717c Mon Sep 17 00:00:00 2001 From: htouvet Date: Fri, 24 Jul 2020 12:14:48 +0200 Subject: [PATCH] WIP: python3 --- iniparse/__init__.py | 20 ++++---- iniparse/compat.py | 38 +++++++-------- iniparse/config.py | 22 ++++----- iniparse/configparser.py | 7 +++ iniparse/ini.py | 95 ++++++++++++++++++++---------------- iniparse/utils.py | 9 ++-- libtisbackup/backup_mysql.py | 12 ++--- libtisbackup/backup_null.py | 2 +- libtisbackup/backup_rsync.py | 45 ++++++++--------- libtisbackup/common.py | 66 +++++++++++++------------ tisbackup.py | 41 ++++++++-------- 11 files changed, 185 insertions(+), 172 deletions(-) mode change 100755 => 100644 iniparse/__init__.py mode change 100755 => 100644 iniparse/compat.py mode change 100755 => 100644 iniparse/config.py create mode 100644 iniparse/configparser.py mode change 100755 => 100644 iniparse/ini.py mode change 100755 => 100644 iniparse/utils.py diff --git a/iniparse/__init__.py b/iniparse/__init__.py old mode 100755 new mode 100644 index 8de756f..7193f92 --- a/iniparse/__init__.py +++ b/iniparse/__init__.py @@ -3,17 +3,17 @@ # Copyright (c) 2007 Tim Lauridsen # All Rights Reserved. See LICENSE-PSF & LICENSE for details. -from ini import INIConfig, change_comment_syntax -from config import BasicConfig, ConfigNamespace -from compat import RawConfigParser, ConfigParser, SafeConfigParser -from utils import tidy +from .ini import INIConfig, change_comment_syntax +from .config import BasicConfig, ConfigNamespace +from .compat import RawConfigParser, ConfigParser, SafeConfigParser +from .utils import tidy -from ConfigParser import DuplicateSectionError, \ - NoSectionError, NoOptionError, \ - InterpolationMissingOptionError, \ - InterpolationDepthError, \ - InterpolationSyntaxError, \ - DEFAULTSECT, MAX_INTERPOLATION_DEPTH +from .configparser import DuplicateSectionError, \ + NoSectionError, NoOptionError, \ + InterpolationMissingOptionError, \ + InterpolationDepthError, \ + InterpolationSyntaxError, \ + DEFAULTSECT, MAX_INTERPOLATION_DEPTH __all__ = [ 'BasicConfig', 'ConfigNamespace', diff --git a/iniparse/compat.py b/iniparse/compat.py old mode 100755 new mode 100644 index db89ed8..76eb1ea --- a/iniparse/compat.py +++ b/iniparse/compat.py @@ -12,19 +12,22 @@ The underlying INIConfig object can be accessed as cfg.data """ import re -from ConfigParser import DuplicateSectionError, \ - NoSectionError, NoOptionError, \ - InterpolationMissingOptionError, \ - InterpolationDepthError, \ - InterpolationSyntaxError, \ - DEFAULTSECT, MAX_INTERPOLATION_DEPTH +from .configparser import DuplicateSectionError, \ + NoSectionError, NoOptionError, \ + InterpolationMissingOptionError, \ + InterpolationDepthError, \ + InterpolationSyntaxError, \ + DEFAULTSECT, MAX_INTERPOLATION_DEPTH # These are imported only for compatiability. # The code below does not reference them directly. -from ConfigParser import Error, InterpolationError, \ - MissingSectionHeaderError, ParsingError +from .configparser import Error, InterpolationError, \ + MissingSectionHeaderError, ParsingError + +import six + +from . import ini -import ini class RawConfigParser(object): def __init__(self, defaults=None, dict_type=dict): @@ -56,7 +59,7 @@ class RawConfigParser(object): # The default section is the only one that gets the case-insensitive # treatment - so it is special-cased here. if section.lower() == "default": - raise ValueError, 'Invalid section name: %s' % section + raise ValueError('Invalid section name: %s' % section) if self.has_section(section): raise DuplicateSectionError(section) @@ -68,7 +71,7 @@ class RawConfigParser(object): The DEFAULT section is not acknowledged. """ - return (section in self.data) + return section in self.data def options(self, section): """Return a list of option names for the given section name.""" @@ -88,7 +91,7 @@ class RawConfigParser(object): filename may also be given. """ files_read = [] - if isinstance(filenames, basestring): + if isinstance(filenames, six.string_types): filenames = [filenames] for filename in filenames: try: @@ -113,8 +116,6 @@ class RawConfigParser(object): def get(self, section, option, vars=None): if not self.has_section(section): raise NoSectionError(section) - if vars is not None and option in vars: - value = vars[option] sec = self.data[section] if option in sec: @@ -143,7 +144,7 @@ class RawConfigParser(object): def getboolean(self, section, option): v = self.get(section, option) if v.lower() not in self._boolean_states: - raise ValueError, 'Not a boolean: %s' % v + raise ValueError('Not a boolean: %s' % v) return self._boolean_states[v.lower()] def has_option(self, section, option): @@ -234,7 +235,7 @@ class ConfigParser(RawConfigParser): if "%(" in value: try: value = value % vars - except KeyError, e: + except KeyError as e: raise InterpolationMissingOptionError( option, section, rawval, e.args[0]) else: @@ -283,7 +284,7 @@ class SafeConfigParser(ConfigParser): _badpercent_re = re.compile(r"%[^%]|%$") def set(self, section, option, value): - if not isinstance(value, basestring): + if not isinstance(value, six.string_types): raise TypeError("option values must be strings") # check for bad percent signs: # first, replace all "good" interpolations @@ -323,8 +324,7 @@ class SafeConfigParser(ConfigParser): elif c == "(": m = self._interpvar_match(rest) if m is None: - raise InterpolationSyntaxError(option, section, - "bad interpolation variable reference %r" % rest) + raise InterpolationSyntaxError(option, section, "bad interpolation variable reference %r" % rest) var = m.group(1) rest = rest[m.end():] try: diff --git a/iniparse/config.py b/iniparse/config.py old mode 100755 new mode 100644 index 5cfa2ea..d0b8423 --- a/iniparse/config.py +++ b/iniparse/config.py @@ -86,6 +86,7 @@ class ConfigNamespace(object): def __setstate__(self, state): self.__dict__.update(state) + class Undefined(object): """Helper class used to hold undefined names until assignment. @@ -143,16 +144,16 @@ class BasicConfig(ConfigNamespace): >>> n.aaa = 42 >>> del n.x - >>> print n + >>> print(n) aaa = 42 name.first = paramjit name.last = oberoi - Nested namepsaces are also namespaces: + Nested namespaces are also namespaces: >>> isinstance(n.name, ConfigNamespace) True - >>> print n.name + >>> print(n.name) first = paramjit last = oberoi >>> sorted(list(n.name)) @@ -160,7 +161,7 @@ class BasicConfig(ConfigNamespace): Finally, values can be read from a file as follows: - >>> from StringIO import StringIO + >>> from six import StringIO >>> sio = StringIO(''' ... # comment ... ui.height = 100 @@ -171,7 +172,7 @@ class BasicConfig(ConfigNamespace): ... ''') >>> n = BasicConfig() >>> n._readfp(sio) - >>> print n + >>> print(n) complexity = medium data.secret.password = goodness=gracious me have_python @@ -199,7 +200,7 @@ class BasicConfig(ConfigNamespace): def __str__(self, prefix=''): lines = [] - keys = self._data.keys() + keys = list(self._data.keys()) keys.sort() for name in keys: value = self._data[name] @@ -258,7 +259,7 @@ def update_config(target, source): >>> n.ui.display_clock = True >>> n.ui.display_qlength = True >>> n.ui.width = 150 - >>> print n + >>> print(n) playlist.expand_playlist = True ui.display_clock = True ui.display_qlength = True @@ -267,7 +268,7 @@ def update_config(target, source): >>> from iniparse import ini >>> i = ini.INIConfig() >>> update_config(i, n) - >>> print i + >>> print(i) [playlist] expand_playlist = True @@ -277,7 +278,7 @@ def update_config(target, source): width = 150 """ - for name in source: + for name in sorted(source): value = source[name] if isinstance(value, ConfigNamespace): if name in target: @@ -289,6 +290,3 @@ def update_config(target, source): update_config(myns, value) else: target[name] = value - - - diff --git a/iniparse/configparser.py b/iniparse/configparser.py new file mode 100644 index 0000000..c543d50 --- /dev/null +++ b/iniparse/configparser.py @@ -0,0 +1,7 @@ +try: + from ConfigParser import * + # not all objects get imported with __all__ + from ConfigParser import Error, InterpolationMissingOptionError +except ImportError: + from configparser import * + from configparser import Error, InterpolationMissingOptionError diff --git a/iniparse/ini.py b/iniparse/ini.py old mode 100755 new mode 100644 index 68dd65c..998fb80 --- a/iniparse/ini.py +++ b/iniparse/ini.py @@ -7,7 +7,7 @@ Example: - >>> from StringIO import StringIO + >>> from six import StringIO >>> sio = StringIO('''# configure foo-application ... [foo] ... bar1 = qualia @@ -16,14 +16,14 @@ Example: ... special = 1''') >>> cfg = INIConfig(sio) - >>> print cfg.foo.bar1 + >>> print(cfg.foo.bar1) qualia - >>> print cfg['foo-ext'].special + >>> print(cfg['foo-ext'].special) 1 >>> cfg.foo.newopt = 'hi!' >>> cfg.baz.enabled = 0 - >>> print cfg + >>> print(cfg) # configure foo-application [foo] bar1 = qualia @@ -42,9 +42,12 @@ Example: # Backward-compatiable with ConfigParser import re -from ConfigParser import DEFAULTSECT, ParsingError, MissingSectionHeaderError +from .configparser import DEFAULTSECT, ParsingError, MissingSectionHeaderError + +import six + +from . import config -import config class LineType(object): line = None @@ -73,10 +76,10 @@ class LineType(object): class SectionLine(LineType): - regex = re.compile(r'^\[' - r'(?P[^]]+)' - r'\]\s*' - r'((?P;|#)(?P.*))?$') + regex = re.compile(r'^\[' + r'(?P[^]]+)' + r'\]\s*' + r'((?P;|#)(?P.*))?$') def __init__(self, name, comment=None, comment_separator=None, comment_offset=-1, line=None): @@ -170,8 +173,9 @@ def change_comment_syntax(comment_chars='%;#', allow_rem=False): regex += r')(?P.*)$' CommentLine.regex = re.compile(regex) + class CommentLine(LineType): - regex = re.compile(r'^(?P[;#]|[rR][eE][mM] +)' + regex = re.compile(r'^(?P[;#]|[rR][eE][mM])' r'(?P.*)$') def __init__(self, comment='', separator='#', line=None): @@ -187,6 +191,7 @@ class CommentLine(LineType): if m is None: return None return cls(m.group('comment'), m.group('csep'), line) + parse = classmethod(parse) @@ -195,11 +200,13 @@ class EmptyLine(LineType): def to_string(self): return '' - value = property(lambda _: '') + value = property(lambda self: '') def parse(cls, line): - if line.strip(): return None + if line.strip(): + return None return cls(line) + parse = classmethod(parse) @@ -221,6 +228,7 @@ class ContinuationLine(LineType): if m is None: return None return cls(m.group('value'), m.start('value'), line) + parse = classmethod(parse) @@ -275,6 +283,7 @@ class LineContainer(object): self.add(EmptyLine()) name = property(get_name, set_name) + value = property(get_value, set_value) def __str__(self): @@ -322,8 +331,8 @@ class INISection(config.ConfigNamespace): _optionxformvalue = None _optionxformsource = None _compat_skip_empty_lines = set() - def __init__(self, lineobj, defaults = None, - optionxformvalue=None, optionxformsource=None): + + def __init__(self, lineobj, defaults=None, optionxformvalue=None, optionxformsource=None): self._lines = [lineobj] self._defaults = defaults self._optionxformvalue = optionxformvalue @@ -453,6 +462,7 @@ class INIConfig(config.ConfigNamespace): _sectionxformsource = None _parse_exc = None _bom = False + def __init__(self, fp=None, defaults=None, parse_exc=True, optionxformvalue=lower, optionxformsource=None, sectionxformvalue=None, sectionxformsource=None): @@ -465,7 +475,7 @@ class INIConfig(config.ConfigNamespace): self._sections = {} if defaults is None: defaults = {} self._defaults = INISection(LineContainer(), optionxformsource=self) - for name, value in defaults.iteritems(): + for name, value in defaults.items(): self._defaults[name] = value if fp is not None: self._readfp(fp) @@ -545,34 +555,34 @@ class INIConfig(config.ConfigNamespace): fname = fp.name except AttributeError: fname = '' - linecount = 0 + line_count = 0 exc = None line = None for line in readline_iterator(fp): # Check for BOM on first line - if linecount == 0 and isinstance(line, unicode): + if line_count == 0 and isinstance(line, six.text_type): if line[0] == u'\ufeff': line = line[1:] self._bom = True - lineobj = self._parse(line) - linecount += 1 + line_obj = self._parse(line) + line_count += 1 - if not cur_section and not isinstance(lineobj, - (CommentLine, EmptyLine, SectionLine)): + if not cur_section and not isinstance(line_obj, (CommentLine, EmptyLine, SectionLine)): if self._parse_exc: - raise MissingSectionHeaderError(fname, linecount, line) + raise MissingSectionHeaderError(fname, line_count, line) else: - lineobj = make_comment(line) + line_obj = make_comment(line) - if lineobj is None: + if line_obj is None: if self._parse_exc: - if exc is None: exc = ParsingError(fname) - exc.append(linecount, line) - lineobj = make_comment(line) + if exc is None: + exc = ParsingError(fname) + exc.append(line_count, line) + line_obj = make_comment(line) - if isinstance(lineobj, ContinuationLine): + if isinstance(line_obj, ContinuationLine): if cur_option: if pending_lines: cur_option.extend(pending_lines) @@ -580,20 +590,21 @@ class INIConfig(config.ConfigNamespace): if pending_empty_lines: optobj._compat_skip_empty_lines.add(cur_option_name) pending_empty_lines = False - cur_option.add(lineobj) + cur_option.add(line_obj) else: # illegal continuation line - convert to comment if self._parse_exc: - if exc is None: exc = ParsingError(fname) - exc.append(linecount, line) - lineobj = make_comment(line) + if exc is None: + exc = ParsingError(fname) + exc.append(line_count, line) + line_obj = make_comment(line) - if isinstance(lineobj, OptionLine): + if isinstance(line_obj, OptionLine): if pending_lines: cur_section.extend(pending_lines) pending_lines = [] pending_empty_lines = False - cur_option = LineContainer(lineobj) + cur_option = LineContainer(line_obj) cur_section.add(cur_option) if self._optionxform: cur_option_name = self._optionxform(cur_option.name) @@ -605,11 +616,11 @@ class INIConfig(config.ConfigNamespace): optobj = self._sections[cur_section_name] optobj._options[cur_option_name] = cur_option - if isinstance(lineobj, SectionLine): + if isinstance(line_obj, SectionLine): self._data.extend(pending_lines) pending_lines = [] pending_empty_lines = False - cur_section = LineContainer(lineobj) + cur_section = LineContainer(line_obj) self._data.add(cur_section) cur_option = None cur_option_name = None @@ -628,16 +639,14 @@ class INIConfig(config.ConfigNamespace): else: self._sections[cur_section_name]._lines.append(cur_section) - if isinstance(lineobj, (CommentLine, EmptyLine)): - pending_lines.append(lineobj) - if isinstance(lineobj, EmptyLine): + if isinstance(line_obj, (CommentLine, EmptyLine)): + pending_lines.append(line_obj) + if isinstance(line_obj, EmptyLine): pending_empty_lines = True self._data.extend(pending_lines) - if line and line[-1]=='\n': + if line and line[-1] == '\n': self._data.add(EmptyLine()) if exc: raise exc - - diff --git a/iniparse/utils.py b/iniparse/utils.py old mode 100755 new mode 100644 index 829fc28..ee5e40d --- a/iniparse/utils.py +++ b/iniparse/utils.py @@ -1,5 +1,6 @@ -import compat -from ini import LineContainer, EmptyLine +from . import compat +from .ini import LineContainer, EmptyLine + def tidy(cfg): """Clean up blank lines. @@ -32,12 +33,12 @@ def tidy(cfg): if cont and not isinstance(cont[-1], EmptyLine): cont.append(EmptyLine()) + def tidy_section(lc): cont = lc.contents i = 1 while i < len(cont): - if (isinstance(cont[i-1], EmptyLine) and - isinstance(cont[i], EmptyLine)): + if isinstance(cont[i-1], EmptyLine) and isinstance(cont[i], EmptyLine): del cont[i] else: i += 1 diff --git a/libtisbackup/backup_mysql.py b/libtisbackup/backup_mysql.py index 1890427..62caa72 100644 --- a/libtisbackup/backup_mysql.py +++ b/libtisbackup/backup_mysql.py @@ -24,13 +24,13 @@ import sys try: sys.stderr = open('/dev/null') # Silence silly warnings from paramiko import paramiko -except ImportError,e: - print "Error : can not load paramiko library %s" % e +except ImportError as e: + print("Error : can not load paramiko library %s" % e) raise sys.stderr = sys.__stderr__ -from common import * +from libtisbackup.common import * class backup_mysql(backup_generic): """Backup a mysql database as gzipped sql file through ssh""" @@ -52,7 +52,7 @@ class backup_mysql(backup_generic): if not self.dry_run: os.makedirs(self.dest_dir) else: - print 'mkdir "%s"' % self.dest_dir + print('mkdir "%s"' % self.dest_dir) else: raise Exception('backup destination directory already exists : %s' % self.dest_dir) @@ -100,7 +100,7 @@ class backup_mysql(backup_generic): self.logger.debug('[%s] Dump DB : %s',self.backup_name,cmd) if not self.dry_run: (error_code,output) = ssh_exec(cmd,ssh=self.ssh) - print output + print(output) self.logger.debug("[%s] Output of %s :\n%s",self.backup_name,cmd,output) if error_code: raise Exception('Aborting, Not null exit code (%i) for "%s"' % (error_code,cmd)) @@ -175,4 +175,4 @@ class backup_mysql(backup_generic): self.logger.info('Skipping %s, already registered',dir_name) -register_driver(backup_mysql) \ No newline at end of file +register_driver(backup_mysql) diff --git a/libtisbackup/backup_null.py b/libtisbackup/backup_null.py index f4d92b3..b9f9871 100755 --- a/libtisbackup/backup_null.py +++ b/libtisbackup/backup_null.py @@ -20,7 +20,7 @@ import os import datetime -from common import * +from libtisbackup.common import * class backup_null(backup_generic): diff --git a/libtisbackup/backup_rsync.py b/libtisbackup/backup_rsync.py index 43723ec..798444f 100644 --- a/libtisbackup/backup_rsync.py +++ b/libtisbackup/backup_rsync.py @@ -20,7 +20,7 @@ import os import datetime -from common import * +from libtisbackup.common import * import time import logging import re @@ -69,7 +69,7 @@ class backup_rsync(backup_generic): if not self.dry_run: os.makedirs(dest_dir) else: - print 'mkdir "%s"' % dest_dir + print('mkdir "%s"' % dest_dir) else: raise Exception('backup destination directory already exists : %s' % dest_dir) @@ -80,7 +80,7 @@ class backup_rsync(backup_generic): if self.dry_run: options.append('-d') - if self.overload_args <> None: + if self.overload_args is not None: options.append(self.overload_args) elif not "cygdrive" in self.remote_dir: # we don't preserve owner, group, links, hardlinks, perms for windows/cygwin as it is not reliable nor useful @@ -118,7 +118,7 @@ class backup_rsync(backup_generic): try: # newsettings with exclude_list='too','titi', parsed as a str python list content excludes = eval('[%s]' % self.exclude_list) - except Exception,e: + except Exception as e: raise Exception('Error reading exclude list : value %s, eval error %s (don\'t forget quotes and comma...)' % (self.exclude_list,e)) options.extend(['--exclude="%s"' % x for x in excludes]) @@ -146,13 +146,13 @@ class backup_rsync(backup_generic): ssh_params.append('-i %s' % self.private_key) if self.cipher_spec: ssh_params.append('-c %s' % self.cipher_spec) - if self.ssh_port <> 22: + if self.ssh_port != 22: ssh_params.append('-p %i' % self.ssh_port) options.append('-e "/usr/bin/ssh %s"' % (" ".join(ssh_params))) backup_source = '%s@%s:%s' % (self.remote_user,self.server_name,self.remote_dir) # ensure there is a slash at end - if backup_source[-1] <> '/': + if backup_source[-1] != '/': backup_source += '/' options_params = " ".join(options) @@ -165,25 +165,20 @@ class backup_rsync(backup_generic): process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) def ondata(data,context): if context.verbose: - print data + print(data) context.logger.debug(data) log = monitor_stdout(process,ondata,self) - reg_total_files = re.compile('Number of files: (?P\d+)') - reg_transferred_files = re.compile('Number of .*files transferred: (?P\d+)') for l in log.splitlines(): - line = l.replace(',','') - m = reg_total_files.match(line) - if m: - stats['total_files_count'] += int(m.groupdict()['file']) - m = reg_transferred_files.match(line) - if m: - stats['written_files_count'] += int(m.groupdict()['file']) - if line.startswith('Total file size:'): - stats['total_bytes'] += int(line.split(':')[1].split()[0]) - if line.startswith('Total transferred file size:'): - stats['written_bytes'] += int(line.split(':')[1].split()[0]) + if l.startswith('Number of files:'): + stats['total_files_count'] += int(re.sub("[^0-9]", "", l.split(':')[1])) + if l.startswith('Number of files transferred:'): + stats['written_files_count'] += int(re.sub("[^0-9]", "", l.split(':')[1])) + if l.startswith('Total file size:'): + stats['total_bytes'] += int(re.sub("[^0-9]", "", l.split(':')[1].split()[0])) + if l.startswith('Total transferred file size:'): + stats['written_bytes'] += int(re.sub("[^0-9]", "", l.split(':')[1].split()[0])) returncode = process.returncode ## deal with exit code 24 (file vanished) @@ -195,7 +190,7 @@ class backup_rsync(backup_generic): self.logger.error("[" + self.backup_name + "] shell program exited with error code " + str(returncode)) raise Exception("[" + self.backup_name + "] shell program exited with error code " + str(returncode), cmd, log[-512:]) else: - print cmd + print(cmd) #we suppress the .rsync suffix if everything went well finaldest = os.path.join(self.backup_dir,self.backup_start_date) @@ -203,14 +198,14 @@ class backup_rsync(backup_generic): if not self.dry_run: os.rename(dest_dir, finaldest) self.logger.debug("[%s] touching datetime of target directory %s" ,self.backup_name,finaldest) - print os.popen('touch "%s"' % finaldest).read() + print(os.popen('touch "%s"' % finaldest).read()) else: - print "mv" ,dest_dir,finaldest + print("mv" ,dest_dir,finaldest) stats['backup_location'] = finaldest stats['status']='OK' stats['log']='ssh+rsync backup from %s OK, %d bytes written for %d changed files' % (backup_source,stats['written_bytes'],stats['written_files_count']) - except BaseException , e: + except BaseException as e: stats['status']='ERROR' stats['log']=str(e) raise @@ -340,5 +335,5 @@ if __name__=='__main__': b = backup_rsync('htouvet','/backup/data/htouvet',dbstat) b.read_config(cp) b.process_backup() - print b.checknagios() + print(b.checknagios()) diff --git a/libtisbackup/common.py b/libtisbackup/common.py index 0d67e8f..cca9072 100644 --- a/libtisbackup/common.py +++ b/libtisbackup/common.py @@ -28,14 +28,15 @@ from iniparse import ConfigParser import sqlite3 import shutil import select +import traceback import sys try: sys.stderr = open('/dev/null') # Silence silly warnings from paramiko import paramiko -except ImportError,e: - print "Error : can not load paramiko library %s" % e +except ImportError as e: + print("Error : can not load paramiko library %s" % e) raise sys.stderr = sys.__stderr__ @@ -121,7 +122,7 @@ def check_string(test_string): pattern = r'[^\.A-Za-z0-9\-_]' if re.search(pattern, test_string): #Character other then . a-z 0-9 was found - print 'Invalid : %r' % (test_string,) + print('Invalid : %r' % (test_string,)) def convert_bytes(bytes): if bytes is None: @@ -227,7 +228,7 @@ def monitor_stdout(aprocess, onoutputdata,context): assert(isinstance(aprocess,subprocess.Popen)) read_set = [] stdout = [] - line = '' + line = b'' if aprocess.stdout: read_set.append(aprocess.stdout) @@ -237,7 +238,7 @@ def monitor_stdout(aprocess, onoutputdata,context): while read_set: try: rlist, wlist, xlist = select.select(read_set, [], []) - except select.error, e: + except select.error as e: if e.args[0] == errno.EINTR: continue raise @@ -245,38 +246,38 @@ def monitor_stdout(aprocess, onoutputdata,context): # Reads one line from stdout if aprocess.stdout in rlist: data = os.read(aprocess.stdout.fileno(), 1) - if data == "": + if data == b"": aprocess.stdout.close() read_set.remove(aprocess.stdout) - while data and not data in ('\n','\r'): + while data and not data in (b'\n',b'\r'): line += data data = os.read(aprocess.stdout.fileno(), 1) - if line or data in ('\n','\r'): - stdout.append(line) + if line or data in (b'\n',b'\r'): + stdout.append(line.decode('utf8')) if onoutputdata: - onoutputdata(line,context) - line='' + onoutputdata(line.decode('utf8'),context) + line=b'' # Reads one line from stderr if aprocess.stderr in rlist: data = os.read(aprocess.stderr.fileno(), 1) - if data == "": + if data == b"": aprocess.stderr.close() read_set.remove(aprocess.stderr) - while data and not data in ('\n','\r'): + while data and not data in (b'\n',b'\r'): line += data data = os.read(aprocess.stderr.fileno(), 1) - if line or data in ('\n','\r'): - stdout.append(line) + if line or data in (b'\n',b'\r'): + stdout.append(line.decode('utf8')) if onoutputdata: - onoutputdata(line,context) - line='' + onoutputdata(line.decode('utf8'),context) + line=b'' aprocess.wait() if line: - stdout.append(line) + stdout.append(line.decode('utf8')) if onoutputdata: - onoutputdata(line,context) + onoutputdata(line.decode('utf8'),context) return "\n".join(stdout) @@ -442,7 +443,7 @@ CREATE INDEX idx_stats_backup_name_start on stats(backup_name,backup_start);""") return value #for r in self.query('select * from stats where backup_name=? order by backup_end desc limit ?',(backup_name,count)): - print pp(cur,None,1,fcb) + print(pp(cur,None,1,fcb)) def fcb(self,fields,fieldname,value): @@ -544,11 +545,12 @@ class backup_generic: 'optional':",".join(cls.optional_params)} def check_required_params(self): - for name in self.required_params: - if not hasattr(self,name) or not getattr(self,name): - raise Exception('[%s] Config Attribute %s is required' % (self.backup_name,name)) - if (self.preexec or self.postexec) and (not self.private_key or not self.remote_user): - raise Exception('[%s] remote_user and private_key file required if preexec or postexec is used' % self.backup_name) + #for name in self.required_params: + # if not hasattr(self,name) or not getattr(self,name): + # raise Exception('[%s] Config Attribute %s is required' % (self.backup_name,name)) + #if (self.preexec or self.postexec) and (not self.private_key or not self.remote_user): + # raise Exception('[%s] remote_user and private_key file required if preexec or postexec is used' % self.backup_name) + pass def read_config(self,iniconf): @@ -695,7 +697,7 @@ class backup_generic: self.logger.info('[%s] ######### Backup finished : %s',self.backup_name,stats['log']) return stats - except BaseException, e: + except BaseException as e: stats['status']='ERROR' stats['log']=str(e) endtime = time.time() @@ -713,6 +715,8 @@ class backup_generic: backup_location=stats['backup_location']) self.logger.error('[%s] ######### Backup finished with ERROR: %s',self.backup_name,stats['log']) + self.logger.debug(traceback.format_exc()) + raise @@ -797,7 +801,7 @@ class backup_generic: if not self.dry_run: self.dbstat.db.execute('update stats set TYPE="CLEAN" where backup_name=? and backup_location=?',(self.backup_name,oldbackup_location)) self.dbstat.db.commit() - except BaseException,e: + except BaseException as e: self.logger.error('cleanup_backup : Unable to remove directory/file "%s". Error %s', oldbackup_location,e) removed.append((self.backup_name,oldbackup_location)) else: @@ -845,9 +849,9 @@ class backup_generic: raise Exception('Backup source %s doesn\'t exists' % backup_source) # ensure there is a slash at end - if os.path.isdir(backup_source) and backup_source[-1] <> '/': + if os.path.isdir(backup_source) and backup_source[-1] != '/': backup_source += '/' - if backup_dest[-1] <> '/': + if backup_dest[-1] != '/': backup_dest += '/' if not os.path.isdir(backup_dest): @@ -873,7 +877,7 @@ class backup_generic: process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) def ondata(data,context): if context.verbose: - print data + print(data) context.logger.debug(data) log = monitor_stdout(process,ondata,self) @@ -897,7 +901,7 @@ class backup_generic: self.logger.error("[" + self.backup_name + "] shell program exited with error code ") raise Exception("[" + self.backup_name + "] shell program exited with error code " + str(returncode), cmd) else: - print cmd + print(cmd) stats['status']='OK' self.logger.info('export backup from %s to %s OK, %d bytes written for %d changed files' % (backup_source,backup_dest,stats['written_bytes'],stats['written_files_count'])) diff --git a/tisbackup.py b/tisbackup.py index c4ca88b..1449dbe 100755 --- a/tisbackup.py +++ b/tisbackup.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # -*- coding: utf-8 -*- # ----------------------------------------------------------------------- # This file is part of TISBackup @@ -32,18 +32,17 @@ from libtisbackup.common import * from libtisbackup.backup_mysql import backup_mysql from libtisbackup.backup_rsync import backup_rsync from libtisbackup.backup_rsync import backup_rsync_ssh -from libtisbackup.backup_oracle import backup_oracle -from libtisbackup.backup_rsync_btrfs import backup_rsync_btrfs -from libtisbackup.backup_rsync_btrfs import backup_rsync__btrfs_ssh -from libtisbackup.backup_pgsql import backup_pgsql -from libtisbackup.backup_xva import backup_xva -from libtisbackup.backup_vmdk import backup_vmdk -from libtisbackup.backup_switch import backup_switch +#from libtisbackup.backup_oracle import backup_oracle +#from libtisbackup.backup_rsync_btrfs import backup_rsync_btrfs +#from libtisbackup.backup_rsync_btrfs import backup_rsync__btrfs_ssh +#from libtisbackup.backup_pgsql import backup_pgsql +#from libtisbackup.backup_xva import backup_xva +#from libtisbackup.backup_switch import backup_switch from libtisbackup.backup_null import backup_null -from libtisbackup.backup_xcp_metadata import backup_xcp_metadata -from libtisbackup.copy_vm_xcp import copy_vm_xcp -from libtisbackup.backup_sqlserver import backup_sqlserver -from libtisbackup.backup_samba4 import backup_samba4 +#from libtisbackup.backup_xcp_metadata import backup_xcp_metadata +#from libtisbackup.copy_vm_xcp import copy_vm_xcp +#from libtisbackup.backup_sqlserver import backup_sqlserver +#from libtisbackup.backup_samba4 import backup_samba4 usage="""\ %prog -c configfile action @@ -176,15 +175,15 @@ class tis_backup: nagiosoutput = 'ALL backups OK %s' % (','.join(sections)) - except BaseException,e: + except BaseException as e: worst_nagiosstatus = nagiosStateCritical nagiosoutput = 'EXCEPTION',"Critical : %s" % str(e) raise finally: self.logger.debug('worst nagios status :"%i"',worst_nagiosstatus) - print '%s (tisbackup V%s)' %(nagiosoutput,version) - print '\n'.join(["[%s]:%s" % (l[0],l[1]) for l in globallog]) + print('%s (tisbackup V%s)' %(nagiosoutput,version)) + print('\n'.join(["[%s]:%s" % (l[0],l[1]) for l in globallog])) sys.exit(worst_nagiosstatus) def process_backup(self,sections=[]): @@ -201,7 +200,7 @@ class tis_backup: self.logger.info('Processing [%s]',(backup_item.backup_name)) stats = backup_item.process_backup() processed.append((backup_item.backup_name,stats)) - except BaseException,e: + except BaseException as e: self.logger.critical('Backup [%s] processed with error : %s',backup_item.backup_name,e) errors.append((backup_item.backup_name,str(e))) if not processed and not errors: @@ -227,7 +226,7 @@ class tis_backup: self.logger.info('Processing [%s]',(backup_item.backup_name)) stats = backup_item.export_latestbackup(destdir=exportdir) processed.append((backup_item.backup_name,stats)) - except BaseException,e: + except BaseException as e: self.logger.critical('Export Backup [%s] processed with error : %s',backup_item.backup_name,e) errors.append((backup_item.backup_name,str(e))) if not processed and not errors: @@ -262,7 +261,7 @@ class tis_backup: self.logger.info('Processing [%s]',(backup_item.backup_name)) stats = backup_item.process_backup() processed.append((backup_item.backup_name,stats)) - except BaseException,e: + except BaseException as e: self.logger.critical('Backup [%s] not processed, error : %s',backup_item.backup_name,e) errors.append((backup_item.backup_name,str(e))) if not processed and not errors: @@ -290,7 +289,7 @@ class tis_backup: self.logger.info('Processing cleanup of [%s]',(backup_item.backup_name)) backup_item.cleanup_backup() processed = True - except BaseException,e: + except BaseException as e: self.logger.critical('Cleanup of [%s] not processed, error : %s',backup_item.backup_name,e) if not processed: self.logger.critical('No cleanup properly finished or processed') @@ -322,7 +321,7 @@ def main(): (options,args)=parser.parse_args() if len(args) != 1: - print "ERROR : You must provide one action to perform" + print("ERROR : You must provide one action to perform") parser.print_usage() sys.exit(2) @@ -332,7 +331,7 @@ def main(): action = args[0] if action == "listdrivers": for t in backup_drivers: - print backup_drivers[t].get_help() + print(backup_drivers[t].get_help()) sys.exit(0) config_file =options.config