1 # -*- coding: utf-8 -*-
3 # Copyright 2012 GRNET S.A. All rights reserved.
5 # Redistribution and use in source and binary forms, with or
6 # without modification, are permitted provided that the following
9 # 1. Redistributions of source code must retain the above
10 # copyright notice, this list of conditions and the following
13 # 2. Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following
15 # disclaimer in the documentation and/or other materials
16 # provided with the distribution.
18 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 # POSSIBILITY OF SUCH DAMAGE.
31 # The views and conclusions contained in the software and
32 # documentation are those of the authors and should not be
33 # interpreted as representing official policies, either expressed
34 # or implied, of GRNET S.A.
36 """This module provides an interface for the rsync utility"""
42 from image_creator.util import FatalError
46 """Wrapper class for the rsync command"""
48 def __init__(self, output):
49 """Create an instance """
52 self._options = ['-v']
55 """Enable the archive option"""
56 self._options.append('-a')
60 """Preserve extended attributes"""
61 self._options.append('-X')
65 """Preserve hard links"""
66 self._options.append('-H')
71 self._options.append('-A')
75 """Handle sparse files efficiently"""
76 self._options.append('-S')
79 def exclude(self, pattern):
80 """Add an exclude pattern"""
81 self._exclude.append(pattern)
85 """Reset all rsync options"""
87 self._options = ['-v']
89 def run(self, src, dest, slabel='source', dlabel='destination'):
90 """Run the actual command"""
93 cmd.extend(self._options)
94 for i in self._exclude:
95 cmd.extend(['--exclude', i])
97 self._out.output("Calculating total number of %s files ..." % slabel,
100 # If you don't specify a destination, rsync will list the source files.
101 dry_run = subprocess.Popen(cmd + [src], shell=False,
102 stdout=subprocess.PIPE, bufsize=0)
105 for _ in iter(dry_run.stdout.readline, b''):
108 dry_run.communicate()
109 if dry_run.returncode != 0:
110 raise FatalError("rsync failed")
112 self._out.success("%d" % total)
114 progress = self._out.Progress(total, "Copying files to %s" % dlabel)
115 run = subprocess.Popen(cmd + [src, dest], shell=False,
116 stdout=subprocess.PIPE, bufsize=0)
120 for _ in iter(run.stdout.readline, b''):
122 current = time.time()
123 if current - t > 0.1:
127 progress.success('done')
130 def handler(signum, frame):
134 if run.returncode is None:
138 signal.signal(signal.SIGALRM, handler)
142 if run.returncode != 0:
143 raise FatalError("rsync failed")
145 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :