summaryrefslogtreecommitdiffstats
path: root/func/minion/modules/filetracker.py
blob: c9ed6ed6988cc744b7e026078c6efce2e4bbf8a5 (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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
## func
##
## filetracker
##  maintains a manifest of files of which to keep track
##  provides file meta-data (and optionally full data) to func-inventory
##
## (C) Vito Laurenza <vitolaurenza@gmail.com>
## + Michael DeHaan <mdehaan@redhat.com>
##
## 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.
##

# func modules
import func_module

# other modules
from stat import *
import fnmatch
import glob
import os
import md5

# defaults
CONFIG_FILE='/etc/func/modules/filetracker.conf'

class FileTracker(func_module.FuncModule):

    version = "0.0.1"
    api_version = "0.0.2"
    description = "Maintains a manifest of files to keep track of."

    def __load(self):
        """
        Parse file and return data structure.
        """

        filehash = {}
        if os.path.exists(CONFIG_FILE):
            config = open(CONFIG_FILE, "r")
            data   = config.read()
            lines  = data.split("\n")
            for line in lines:
                tokens = line.split(None)
                if len(tokens) < 2:
                    continue
                scan_mode = tokens[0]
                path = " ".join(tokens[1:])
                if str(scan_mode).lower() == "0":
                    scan_mode = 0
                else:
                    scan_mode = 1
                filehash[path] = scan_mode
        return filehash

    #==========================================================

    def __save(self, filehash):
        """
        Write data structure to file.
        """ 

        config = open(CONFIG_FILE, "w+")
        for (path, scan_mode) in filehash.iteritems():
            config.write("%s     %s\n" % (scan_mode, path))
        config.close()

    #==========================================================
               
    def track(self, file_name_globs, full_scan=0):
        """
        Adds files to keep track of.
        file_names can be a single filename, a list of filenames, a filename glob
           or a list of filename globs
        full_scan implies tracking the full contents of the file, defaults to off
        """

        filehash = self.__load()
        filenameglobs = []

        # accept a single string or list
        filenameglobs.append(file_name_globs)
        if type(file_name_globs) == type([]):
            filenameglobs = file_name_globs


        # expand everything that might be a glob to a list
        # of names to track
        for filenameglob in filenameglobs:
            filenames = glob.glob(filenameglob)
            for filename in filenames:
                filehash[filename] = full_scan
        self.__save(filehash)
        return 1

    #==========================================================

    def untrack(self, file_name_globs):
        """
        Stop keeping track of a file.
        file_name_globs can be a single filename, a list of filenames, a filename glob
           or a list of filename globs
        This routine is tolerant of most errors since we're forgetting about the file anyway.
        """

        filehash = self.__load()
        filenames = filehash.keys()
        for filename in filenames:
            for file_name_glob in file_name_globs:
                if fnmatch.fnmatch(filename, file_name_glob):
                    del filehash[filename]
        self.__save(filehash)
        return 1

    #==========================================================

    def inventory(self, flatten=1, checksum_enabled=1):
        """
        Returns information on all tracked files
        By default, 'flatten' is passed in as True, which makes printouts very clean in diffs
        for use by func-inventory.  If you are writting another software application, using flatten=False will
        prevent the need to parse the returns.
        """
 
        # XMLRPC feeds us strings from the CLI when it shouldn't
        flatten = int(flatten)
        checksum_enabled = int(checksum_enabled)

        filehash = self.__load()

        # we'll either return a very flat string (for clean diffs)
        # or a data structure
        if flatten:
            results = ""
        else:
            results = []

        for (file_name, scan_type) in filehash.iteritems():

            if not os.path.exists(file_name):
                if flatten:
                    results = results + "%s: does not exist\n" % file_name
                else:
                    results.append("%s: does not exist\n" % file_name)
                continue

            this_result = []

            # ----- always process metadata
            filestat = os.stat(file_name)
            mode = filestat[ST_MODE]
            mtime = filestat[ST_MTIME]
            uid = filestat[ST_UID]
            gid = filestat[ST_GID]
            if not os.path.isdir(file_name) and checksum_enabled:
                sum_handle = open(file_name)
                hash = self.__sumfile(sum_handle)
                sum_handle.close()
            else:
                hash = "N/A"

            # ------ what we return depends on flatten
            if flatten:
                this_result = "%s:  mode=%s  mtime=%s  uid=%s  gid=%s  md5sum=%s\n" % (file_name,mode,mtime,uid,gid,hash) 
            else:
                this_result = [file_name,mode,mtime,uid,gid,hash]

            # ------ add on file data only if requested
            if scan_type != 0 and os.path.isfile(file_name):
                tracked_file = open(file_name)
                data = tracked_file.read()
                if flatten:
                    this_result = this_result + "*** DATA ***\n" + data + "\n*** END DATA ***\n\n"
                else:
                    this_result.append(data)
                tracked_file.close()
           
            if os.path.isdir(file_name):
                if not file_name.endswith("/"):
                    file_name = file_name + "/"
                files = glob.glob(file_name + "*") 
                if flatten:
                    this_result = this_result + "*** FILES ***\n" + "\n".join(files) + "\n*** END FILES ***\n\n"
                else:
                    this_result.append({"files" : files})

            if flatten:
                results = results + "\n" + this_result
            else:
                results.append(this_result)


        return results

    #==========================================================

    def __sumfile(self, fobj):
        """
        Returns an md5 hash for an object with read() method.
        credit: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/266486
        """

        m = md5.new()
        while True:
            d = fobj.read(8096)
            if not d:
                break
            m.update(d)
        return m.hexdigest()

    def register_method_args(self):
        """
        Implementing the argument getter part
        """

        return {
                'inventory':{
                    'args':{
                        'flatten':{
                            'type':'boolean',
                            'optional':True,
                            'default':True,
                            'description':"Show info in clean diffs"
                            },
                        'checksum_enabled':{
                            'type':'boolean',
                            'optional':True,
                            'default':True,
                            'description':"Enable the checksum"
                            }
                        },
                    'description':"Returns information on all tracked files"
                    },
                'track':{
                    'args':{
                        'file_name_globs':{
                            'type':'string',
                            'optional':False,
                            'description':"The file name to track (full path)"
                            },
                        'full_scan':{
                            'type':'int',
                            'optional':True,
                            'default':0,
                            'description':"The 0 is for off and 1 is for on"
                            }
                        },
                    'description':"Adds files to keep track of"
                    },
                'untrack':{
                    'args':{
                        'file_name_globs':{
                            'type':'string',
                            'optional':False,
                            'description':"The file name to untrack (full path)"
                            }
                        },
                    'description':"Remove the track from specified file name"
                    }
                }