Browse Source

Fix logging and roots with ::

Nathaniel van Diepen 4 years ago
parent
commit
7436e3bae4
2 changed files with 72 additions and 27 deletions
  1. 71 26
      backup.py
  2. 1 1
      etc/backup.d/backup.yml

+ 71 - 26
backup.py

@@ -1,6 +1,7 @@
 import contextlib
 import os
 import platform
+import shlex
 import subprocess
 import sys
 import time
@@ -54,7 +55,7 @@ class BackupException(Exception):
 class Job(object):
     pool = []
     maxthreads = 4
-    verbosity = 4
+    verbosity = 3
     logTransitions = False
     READY = 0
     QUEUED = 1
@@ -114,27 +115,47 @@ class Job(object):
 
     @property
     def args(self):
-        if Backup.engine == "rdiff-backup":
-            args = ['rdiff-backup', '-v{}'.format(Backup.verbosity)]
-            if 'filters' in self.config:
-                for item in self.config['filters']:
-                    if 'include' in item:
-                        args += ['--include', item['include']]
+        if not hasattr(self, '_args'):
+            if Backup.engine == "rdiff-backup":
+                args = ['rdiff-backup', '-v{}'.format(Backup.verbosity)]
+                if 'filters' in self.config:
+                    for item in self.config['filters']:
+                        if 'include' in item:
+                            args += ['--include', item['include']]
 
-                    elif 'exclude' in item:
-                        args += ['--exclude', item['exclude']]
+                        elif 'exclude' in item:
+                            args += ['--exclude', item['exclude']]
 
-                    else:
-                        raise BackupException(
-                            '{0} has an invalid filter {1}'.format(self, item))
+                        else:
+                            raise BackupException(
+                                '{0} has an invalid filter {1}'.format(self, item))
+
+                self._args = args + [self.fromPath, self.toPath]
 
-            return args + [self.fromPath, self.toPath]
+            else:
+                raise StateException(
+                    'Invalid backup engine {}'.format(Backup.engine))
 
-        raise StateException('Invalid backup engine {}'.format(Backup.engine))
+        return self._args
 
     @property
     def logfile(self):
-        return self._backup.logfile
+        if not hasattr(self, '_logfile'):
+            path = os.path.dirname(self.logpath)
+            if not os.path.exists(path):
+                os.makedirs(path, exist_ok=True)
+
+            self._logfile = open(self.logpath, 'w')
+
+        return self._logfile
+
+    @property
+    def logpath(self):
+        if not hasattr(self, '_logpath'):
+            self._logpath = os.path.join(os.path.dirname(
+                self._backup.logpath), 'job{}.log'.format(self.index))
+
+        return self._logpath
 
     @property
     def fromPath(self):
@@ -143,7 +164,15 @@ class Job(object):
             if 'roots' in self._backup.config:
                 roots = self._backup.config['roots']
                 if 'from' in roots:
-                    fromPath = os.path.join(roots['from'], fromPath)
+                    if '::' in roots['from']:
+                        if roots['from'].endswith('::'):
+                            fromPath = roots['from'] + fromPath
+
+                        else:
+                            fromPath = roots['from'] + os.sep + fromPath
+
+                    else:
+                        fromPath = os.path.join(roots['from'], fromPath)
 
             self._fromPath = fromPath
 
@@ -156,7 +185,15 @@ class Job(object):
             if 'roots' in self._backup.config:
                 roots = self._backup.config['roots']
                 if 'to' in roots:
-                    toPath = os.path.join(roots['to'], toPath)
+                    if '::' in roots['to']:
+                        if roots['to'].endswith('::'):
+                            toPath = roots['to'] + toPath
+
+                        else:
+                            toPath = roots['to'] + os.sep + toPath
+
+                    else:
+                        toPath = os.path.join(roots['to'], toPath)
 
             self._toPath = toPath
 
@@ -177,8 +214,8 @@ class Job(object):
         text = '[Backup {0} Job #{1}] {2}\n'.format(
                 self._backup.name, self.index, text)
         print(text, end='')
-        self.logfile.write(text)
-        self.logfile.flush()
+        self._backup.logfile.write(text)
+        self._backup.logfile.flush()
 
     def start(self):
         if self.state is not Job.QUEUED:
@@ -186,14 +223,17 @@ class Job(object):
 
         self._backup.setStatus(Backup.RUNNING)
         self.setState(Job.RUNNING)
+        self.logfile.write(' '.join([shlex.quote(x) for x in self.args]) + '\n')
+        self.logfile.flush()
         self._process = subprocess.Popen(
                 self.args, stdout=self.logfile, stderr=subprocess.STDOUT,
                 stdin=subprocess.DEVNULL, universal_newlines=True, bufsize=1)
 
     def setState(self, state):
-        self._state = state
-        self.log('{0} -> {1}'.format(
-                self.getState(self._state), self.getState(state)))
+        if self._state != state:
+            self.log('{0} -> {1}'.format(
+                    self.getState(self._state), self.getState(state)))
+            self._state = state
 
     def getState(self, state=None):
         return {
@@ -269,7 +309,7 @@ class Backup(object):
         self._path = self._config['path']
         self._name = os.path.basename(self._path)
         self._logpath = os.path.realpath(os.path.join(
-            Backup.logdir, '{}.log'.format(slugify(self._path))))
+                Backup.logdir, slugify(self._path), 'backup.log'))
         self._status = Backup.READY
         if self.blocking:
             self.setStatus(Backup.BLOCKED)
@@ -300,6 +340,10 @@ class Backup(object):
     @property
     def logfile(self):
         if not hasattr(self, '_logfile'):
+            path = os.path.dirname(self.logpath)
+            if not os.path.exists(path):
+                os.makedirs(path, exist_ok=True)
+
             self._logfile = open(self.logpath, 'w+')
 
         return self._logfile
@@ -379,9 +423,10 @@ class Backup(object):
         return [x for x in self.jobs if x.state is Job.FAILED]
 
     def setStatus(self, status):
-        self.log('{0} -> {1}'.format(
-            self.getStatus(self._status), self.getStatus(status)))
-        self._status = status
+        if self._status != status:
+            self.log('{0} -> {1}'.format(
+                self.getStatus(self._status), self.getStatus(status)))
+            self._status = status
 
     def getStatus(self, status=None):
         return {

+ 1 - 1
etc/backup.d/backup.yml

@@ -1,6 +1,6 @@
 ---
 engine: rdiff-backup
-verbosity: 4
+verbosity: 3
 maxthreads: 4
 logdir: var/log/backup.d
 sources: