summaryrefslogtreecommitdiffstats
path: root/certmaster/minion/modules/copyfile.py
blob: 150af88b1a8fdddb02dc33a34b68a3051cc69fb3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# Copyright 2007, Red Hat, Inc
# seth vidal
#
# This software may be freely redistributed under the terms of the GNU
# general public license.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


import sha
import os
import time
import shutil

import func_module


class CopyFile(func_module.FuncModule):

    version = "0.0.1"
    api_version = "0.0.2"
    description = "Allows for smart copying of a file."

    def _checksum_blob(self, blob):
        thissum = sha.new()
        thissum.update(blob)
        return thissum.hexdigest()
                       
    def checksum(self, thing):

        CHUNK=2**16
        thissum = sha.new()
        if os.path.exists(thing):
            fo = open(thing, 'r', CHUNK)
            chunk = fo.read
            while chunk:
                chunk = fo.read(CHUNK)
                thissum.update(chunk)
            fo.close()
            del fo
        else:
            # assuming it's a string of some kind
            thissum.update(thing)

        return thissum.hexdigest()


    def copyfile(self, filepath, filebuf, mode=0644, uid=0, gid=0, force=None):
        # -1 = problem file was not copied
        # 1 =  file was copied
        # 0 = file was not copied b/c file is unchanged


        # we should probably verify mode,uid,gid are valid as well

        dirpath = os.path.dirname(filepath)
        if not os.path.exists(dirpath):
            os.makedirs(dirpath)

        remote_sum = self._checksum_blob(filebuf.data)
        local_sum = 0
        if os.path.exists(filepath):
            local_sum = self.checksum(filepath)

        if remote_sum != local_sum or force is not None:
            # back up the localone
            if os.path.exists(filepath):
                if not self._backuplocal(filepath):
                    return -1

            # do the new write
            try:
                fo = open(filepath, 'w')
                fo.write(filebuf.data)
                fo.close()
                del fo
            except (IOError, OSError), e:
                # XXX logger output here
                return -1
        else:
            return 0

        # hmm, need to figure out proper exceptions -akl
        try:
            # we could intify the mode here if it's a string
            os.chmod(filepath, mode)
            os.chown(filepath, uid, gid)
        except (IOError, OSError), e:
            return -1

        return 1

    def _backuplocal(self, fn):
        """
        make a date-marked backup of the specified file,
        return True or False on success or failure
        """
        # backups named basename-YYYY-MM-DD@HH:MM~
        ext = time.strftime("%Y-%m-%d@%H:%M~", time.localtime(time.time()))
        backupdest = '%s.%s' % (fn, ext)

        try:
            shutil.copy2(fn, backupdest)
        except shutil.Error, e:
            #XXX logger output here
            return False
        return True