Statistics
| Branch: | Tag: | Revision:

root / image_creator / rsync.py @ c71f38be

History | View | Annotate | Download (4.6 kB)

1 121f3bc0 Nikos Skalkotos
# -*- coding: utf-8 -*-
2 121f3bc0 Nikos Skalkotos
#
3 74149d07 Nikos Skalkotos
# Copyright 2012 GRNET S.A. All rights reserved.
4 74149d07 Nikos Skalkotos
#
5 74149d07 Nikos Skalkotos
# Redistribution and use in source and binary forms, with or
6 74149d07 Nikos Skalkotos
# without modification, are permitted provided that the following
7 74149d07 Nikos Skalkotos
# conditions are met:
8 74149d07 Nikos Skalkotos
#
9 74149d07 Nikos Skalkotos
#   1. Redistributions of source code must retain the above
10 74149d07 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
11 74149d07 Nikos Skalkotos
#      disclaimer.
12 74149d07 Nikos Skalkotos
#
13 74149d07 Nikos Skalkotos
#   2. Redistributions in binary form must reproduce the above
14 74149d07 Nikos Skalkotos
#      copyright notice, this list of conditions and the following
15 74149d07 Nikos Skalkotos
#      disclaimer in the documentation and/or other materials
16 74149d07 Nikos Skalkotos
#      provided with the distribution.
17 74149d07 Nikos Skalkotos
#
18 74149d07 Nikos Skalkotos
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 74149d07 Nikos Skalkotos
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 74149d07 Nikos Skalkotos
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 74149d07 Nikos Skalkotos
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 74149d07 Nikos Skalkotos
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 74149d07 Nikos Skalkotos
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 74149d07 Nikos Skalkotos
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 74149d07 Nikos Skalkotos
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 74149d07 Nikos Skalkotos
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 74149d07 Nikos Skalkotos
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 74149d07 Nikos Skalkotos
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 74149d07 Nikos Skalkotos
# POSSIBILITY OF SUCH DAMAGE.
30 74149d07 Nikos Skalkotos
#
31 74149d07 Nikos Skalkotos
# The views and conclusions contained in the software and
32 74149d07 Nikos Skalkotos
# documentation are those of the authors and should not be
33 74149d07 Nikos Skalkotos
# interpreted as representing official policies, either expressed
34 74149d07 Nikos Skalkotos
# or implied, of GRNET S.A.
35 74149d07 Nikos Skalkotos
36 121f3bc0 Nikos Skalkotos
"""This module provides an interface for the rsync utility"""
37 121f3bc0 Nikos Skalkotos
38 74149d07 Nikos Skalkotos
import subprocess
39 d15368cc Nikos Skalkotos
import time
40 d15368cc Nikos Skalkotos
import signal
41 d15368cc Nikos Skalkotos
42 d15368cc Nikos Skalkotos
from image_creator.util import FatalError
43 74149d07 Nikos Skalkotos
44 74149d07 Nikos Skalkotos
45 74149d07 Nikos Skalkotos
class Rsync:
46 d15368cc Nikos Skalkotos
    """Wrapper class for the rsync command"""
47 d15368cc Nikos Skalkotos
48 1fa75c4c Nikos Skalkotos
    def __init__(self, output):
49 1fa75c4c Nikos Skalkotos
        """Create an instance """
50 1fa75c4c Nikos Skalkotos
        self._out = output
51 1fa75c4c Nikos Skalkotos
        self._exclude = []
52 1fa75c4c Nikos Skalkotos
        self._options = ['-v']
53 74149d07 Nikos Skalkotos
54 74149d07 Nikos Skalkotos
    def archive(self):
55 d15368cc Nikos Skalkotos
        """Enable the archive option"""
56 1fa75c4c Nikos Skalkotos
        self._options.append('-a')
57 74149d07 Nikos Skalkotos
        return self
58 74149d07 Nikos Skalkotos
59 1fa75c4c Nikos Skalkotos
    def xattrs(self):
60 1fa75c4c Nikos Skalkotos
        """Preserve extended attributes"""
61 1fa75c4c Nikos Skalkotos
        self._options.append('-X')
62 1fa75c4c Nikos Skalkotos
        return self
63 1fa75c4c Nikos Skalkotos
64 1fa75c4c Nikos Skalkotos
    def hard_links(self):
65 1fa75c4c Nikos Skalkotos
        """Preserve hard links"""
66 1fa75c4c Nikos Skalkotos
        self._options.append('-H')
67 1fa75c4c Nikos Skalkotos
        return self
68 1fa75c4c Nikos Skalkotos
69 1fa75c4c Nikos Skalkotos
    def acls(self):
70 1fa75c4c Nikos Skalkotos
        """Preserve ACLs"""
71 1fa75c4c Nikos Skalkotos
        self._options.append('-A')
72 1fa75c4c Nikos Skalkotos
        return self
73 1fa75c4c Nikos Skalkotos
74 1fa75c4c Nikos Skalkotos
    def sparse(self):
75 1fa75c4c Nikos Skalkotos
        """Handle sparse files efficiently"""
76 1fa75c4c Nikos Skalkotos
        self._options.append('-S')
77 1fa75c4c Nikos Skalkotos
        return self
78 1fa75c4c Nikos Skalkotos
79 1fa75c4c Nikos Skalkotos
    def exclude(self, pattern):
80 1fa75c4c Nikos Skalkotos
        """Add an exclude pattern"""
81 1fa75c4c Nikos Skalkotos
        self._exclude.append(pattern)
82 1fa75c4c Nikos Skalkotos
        return self
83 1fa75c4c Nikos Skalkotos
84 1fa75c4c Nikos Skalkotos
    def reset(self):
85 1fa75c4c Nikos Skalkotos
        """Reset all rsync options"""
86 1fa75c4c Nikos Skalkotos
        self._exclude = []
87 1fa75c4c Nikos Skalkotos
        self._options = ['-v']
88 1fa75c4c Nikos Skalkotos
89 63e1b902 Nikos Skalkotos
    def run(self, src, dest, slabel='source', dlabel='destination'):
90 d15368cc Nikos Skalkotos
        """Run the actual command"""
91 74149d07 Nikos Skalkotos
        cmd = []
92 74149d07 Nikos Skalkotos
        cmd.append('rsync')
93 1fa75c4c Nikos Skalkotos
        cmd.extend(self._options)
94 1fa75c4c Nikos Skalkotos
        for i in self._exclude:
95 74149d07 Nikos Skalkotos
            cmd.extend(['--exclude', i])
96 74149d07 Nikos Skalkotos
97 63e1b902 Nikos Skalkotos
        self._out.output("Calculating total number of %s files ..." % slabel,
98 63e1b902 Nikos Skalkotos
                         False)
99 1fa75c4c Nikos Skalkotos
100 1fa75c4c Nikos Skalkotos
        # If you don't specify a destination, rsync will list the source files.
101 1fa75c4c Nikos Skalkotos
        dry_run = subprocess.Popen(cmd + [src], shell=False,
102 1fa75c4c Nikos Skalkotos
                                   stdout=subprocess.PIPE, bufsize=0)
103 d15368cc Nikos Skalkotos
        try:
104 d15368cc Nikos Skalkotos
            total = 0
105 121f3bc0 Nikos Skalkotos
            for _ in iter(dry_run.stdout.readline, b''):
106 d15368cc Nikos Skalkotos
                total += 1
107 d15368cc Nikos Skalkotos
        finally:
108 d15368cc Nikos Skalkotos
            dry_run.communicate()
109 d15368cc Nikos Skalkotos
            if dry_run.returncode != 0:
110 d15368cc Nikos Skalkotos
                raise FatalError("rsync failed")
111 d15368cc Nikos Skalkotos
112 1fa75c4c Nikos Skalkotos
        self._out.success("%d" % total)
113 74149d07 Nikos Skalkotos
114 f2ddf4db Nikos Skalkotos
        progress = self._out.Progress(total, "Copying files to %s" % dlabel)
115 1fa75c4c Nikos Skalkotos
        run = subprocess.Popen(cmd + [src, dest], shell=False,
116 74149d07 Nikos Skalkotos
                               stdout=subprocess.PIPE, bufsize=0)
117 d15368cc Nikos Skalkotos
        try:
118 d15368cc Nikos Skalkotos
            t = time.time()
119 d15368cc Nikos Skalkotos
            i = 0
120 121f3bc0 Nikos Skalkotos
            for _ in iter(run.stdout.readline, b''):
121 d15368cc Nikos Skalkotos
                i += 1
122 d15368cc Nikos Skalkotos
                current = time.time()
123 d15368cc Nikos Skalkotos
                if current - t > 0.1:
124 d15368cc Nikos Skalkotos
                    t = current
125 d15368cc Nikos Skalkotos
                    progress.goto(i)
126 d15368cc Nikos Skalkotos
127 d15368cc Nikos Skalkotos
            progress.success('done')
128 d15368cc Nikos Skalkotos
129 d15368cc Nikos Skalkotos
        finally:
130 fb2d189c Nikos Skalkotos
            def handler(signum, frame):
131 fb2d189c Nikos Skalkotos
                run.terminate()
132 fb2d189c Nikos Skalkotos
                time.sleep(1)
133 fb2d189c Nikos Skalkotos
                run.poll()
134 fb2d189c Nikos Skalkotos
                if run.returncode is None:
135 fb2d189c Nikos Skalkotos
                    run.kill()
136 fb2d189c Nikos Skalkotos
                run.wait()
137 fb2d189c Nikos Skalkotos
138 fb2d189c Nikos Skalkotos
            signal.signal(signal.SIGALRM, handler)
139 fb2d189c Nikos Skalkotos
            signal.alarm(2)
140 d15368cc Nikos Skalkotos
            run.communicate()
141 fb2d189c Nikos Skalkotos
            signal.alarm(0)
142 d15368cc Nikos Skalkotos
            if run.returncode != 0:
143 d15368cc Nikos Skalkotos
                raise FatalError("rsync failed")
144 74149d07 Nikos Skalkotos
145 74149d07 Nikos Skalkotos
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :