commit 1fa75c4ca399555f63fa744ba8e91495ea091135
Author: Nikos Skalkotos <skalkoto@grnet.gr>
Date:   Thu Jan 3 13:19:00 2013 +0200

    Cleanup Rsync class and add extra options
    
    Add options for preserving extended atrributes, hard links, ACLs.

diff --git a/image_creator/bundle_volume.py b/image_creator/bundle_volume.py
index 9cc26d4..ff4861e 100644
--- a/image_creator/bundle_volume.py
+++ b/image_creator/bundle_volume.py
@@ -377,9 +377,15 @@ class BundleVolume(object):
                                          [(mapped[i], filesystem[i].mpoint)
                                          for i in mapped.keys()])
                 exclude = self._to_exclude() + [image]
-                rsync = Rsync('/', target,
-                              map(lambda p: os.path.relpath(p, '/'), exclude))
-                rsync.archive().run(self.out)
+
+                rsync = Rsync(self.out)
+
+                # Excluded paths need to be relative to the source
+                for excl in map(lambda p: os.path.relpath(p, '/'), exclude):
+                    rsync.exclude(excl)
+
+                rsync.archive().hard_links().xattrs().sparse().acls()
+                rsync.run('/', target)
 
                 # We need to replace the old UUID referencies with the new
                 # ones in grub configuration files and /etc/fstab for file
diff --git a/image_creator/rsync.py b/image_creator/rsync.py
index 0c6b639..81476f2 100644
--- a/image_creator/rsync.py
+++ b/image_creator/rsync.py
@@ -41,32 +41,60 @@ from image_creator.util import FatalError
 class Rsync:
     """Wrapper class for the rsync command"""
 
-    def __init__(self, src, dest, exclude=[]):
-        """Create an instance by defining a source, a destinationa and a number
-        of exclude patterns.
-        """
-        self.src = src
-        self.dest = dest
-        self.exclude = exclude
-        self.options = ['-v']
+    def __init__(self, output):
+        """Create an instance """
+        self._out = output
+        self._exclude = []
+        self._options = ['-v']
 
     def archive(self):
         """Enable the archive option"""
-        self.options.append('-a')
+        self._options.append('-a')
         return self
 
-    def run(self, out):
+    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):
         """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])
 
-        out.output("Calculating total number of host files ...", False)
-        dry_run = subprocess.Popen(cmd + ['-n', self.src, self.dest],
-                                   shell=False, stdout=subprocess.PIPE,
-                                   bufsize=0)
+        self._out.output("Calculating total number of host files ...", 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''):
@@ -76,10 +104,11 @@ class Rsync:
             if dry_run.returncode != 0:
                 raise FatalError("rsync failed")
 
-        out.success("%d" % total)
+        self._out.success("%d" % total)
 
-        progress = out.Progress(total, "Copying files into the image ... ")
-        run = subprocess.Popen(cmd + [self.src, self.dest], shell=False,
+        progress = self._out.Progress(total,
+                                     "Copying host files into the image ... ")
+        run = subprocess.Popen(cmd + [src, dest], shell=False,
                                stdout=subprocess.PIPE, bufsize=0)
         try:
             t = time.time()
