Don't check the token if -t is not defined
[snf-image-creator] / image_creator / rsync.py
index 7c869c2..08bc27a 100644 (file)
 # or implied, of GRNET S.A.
 
 import subprocess
+import time
+import signal
+
+from image_creator.util import FatalError
 
 
 class Rsync:
-    def __init__(self, src, dest, exclude=[]):
-        self.src = src
-        self.dest = dest
-        self.exclude = exclude
-        self.options = ['-v']
+    """Wrapper class for the rsync command"""
+
+    def __init__(self, output):
+        """Create an instance """
+        self._out = output
+        self._exclude = []
+        self._options = ['-v']
 
     def archive(self):
-        self.options.append('-a')
+        """Enable the archive option"""
+        self._options.append('-a')
         return self
 
-    def run(self, out, msg):
+    def xattrs(self):
+        """Preserve extended attributes"""
+        self._options.append('-X')
+        return self
+
+    def hard_links(self):
+        """Preserve hard links"""
+        self._options.append('-H')
+        return self
+
+    def acls(self):
+        """Preserve ACLs"""
+        self._options.append('-A')
+        return self
+
+    def sparse(self):
+        """Handle sparse files efficiently"""
+        self._options.append('-S')
+        return self
+
+    def exclude(self, pattern):
+        """Add an exclude pattern"""
+        self._exclude.append(pattern)
+        return self
+
+    def reset(self):
+        """Reset all rsync options"""
+        self._exclude = []
+        self._options = ['-v']
+
+    def run(self, src, dest, slabel='source', dlabel='destination'):
+        """Run the actual command"""
         cmd = []
         cmd.append('rsync')
-        cmd.extend(self.options)
-        for i in self.exclude:
+        cmd.extend(self._options)
+        for i in self._exclude:
             cmd.extend(['--exclude', i])
 
-        dry_run = subprocess.Popen(cmd + ['-n', self.src, self.dest],
-                                   shell=False, stdout=subprocess.PIPE,
-                                   bufsize=0)
-        total = 0
-        for line in iter(dry_run.stdout.readline, b''):
-            total += 1
+        self._out.output("Calculating total number of %s files ..." % slabel,
+                         False)
 
-        progress = out.Progress(total, msg)
-        run = subprocess.Popen(cmd + [self.src, self.dest], shell=False,
+        # If you don't specify a destination, rsync will list the source files.
+        dry_run = subprocess.Popen(cmd + [src], shell=False,
+                                   stdout=subprocess.PIPE, bufsize=0)
+        try:
+            total = 0
+            for line in iter(dry_run.stdout.readline, b''):
+                total += 1
+        finally:
+            dry_run.communicate()
+            if dry_run.returncode != 0:
+                raise FatalError("rsync failed")
+
+        self._out.success("%d" % total)
+
+        progress = self._out.Progress(total, "Copying files to %s" % dlabel)
+        run = subprocess.Popen(cmd + [src, dest], shell=False,
                                stdout=subprocess.PIPE, bufsize=0)
-        for line in iter(run.stdout.readline, b''):
-            progress.next()
+        try:
+            t = time.time()
+            i = 0
+            for line in iter(run.stdout.readline, b''):
+                i += 1
+                current = time.time()
+                if current - t > 0.1:
+                    t = current
+                    progress.goto(i)
+
+            progress.success('done')
+
+        finally:
+            def handler(signum, frame):
+                run.terminate()
+                time.sleep(1)
+                run.poll()
+                if run.returncode is None:
+                    run.kill()
+                run.wait()
 
-        progress.success('done')
+            signal.signal(signal.SIGALRM, handler)
+            signal.alarm(2)
+            run.communicate()
+            signal.alarm(0)
+            if run.returncode != 0:
+                raise FatalError("rsync failed")
 
 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :