File: misc.py

package info (click to toggle)
apt-proxy 1.9.35-0.3
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 616 kB
  • ctags: 712
  • sloc: python: 3,749; sh: 384; makefile: 63
file content (207 lines) | stat: -rw-r--r-- 7,215 bytes parent folder | download | duplicates (2)
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
#
# Copyright (C) 2002 Manuel Estrada Sainz <ranty@debian.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import os, time
from twisted.internet import reactor, defer
from twisted import python

class DomainLogger:
    """
    This class should help us classify messages into domains and levels.

    This way we can select messages by kind and level.

    You just have to set in the configuration file something like:

        debug = db:3 import:8

      Which means that we only want to see messages of domain 'db' and
      level <= 3 and domain 'import' and level <= 8

      There are three special domains:

         all: if enabled all messages will be shown.
         log: is on by default and only the level can be changed
              it is meant for production logging.
         debug: aptProxyConfig will define it if you select any loging
                domains.

    Pretended meaning of levels:
       0: nothing or maybe critical information
       1: important information
       ...
       9: useless information
    """
    def __init__(self, enabled={'all':9}):
        self.enabled = enabled

    def setDomains(self, domains):
        self.enabled = domains

    def addDomains(self, domains):
        self.enabled.update(domains)
        #print "enabled: ", self.enabled
    def isEnabled(self, domain, level=9):
        domains = self.enabled.keys()
        if domain in domains and level > self.enabled[domain]:
            return 0
        if(('all' in domains and level <= self.enabled['all'])
           or (domain in domains and level <= self.enabled[domain])):
            return 1
        else:
            return 0

    def msg(self, msg, domain='log', level=4):
        "Logs 'msg' if domain and level are appropriate"
        #print 'domain:', domain, 'level:', level
        if self.isEnabled(domain, level):
            try:
                python.log.msg("[%s] %s"%(domain, msg))
            except IOError:
                pass
    def debug(self, msg, domain='debug', level=9):
        "Useful to save typing on new debuging messages"
        if self.isEnabled(domain, level):
            try:
                python.log.msg("[%s] %s"%(domain, msg), debug=True)
            except IOError:
                pass
    def err(self, msg, domain='error', level=9):
        "Log an error message"
        try:
            python.log.err("[%s] %s"%(domain, msg))
        except IOError:
            pass
        

# Prevent log being replace on reload.  This only works in cpython.
try:
    log
except NameError:
    log = DomainLogger()



class MirrorRecycler:
    """
    Reads the mirror tree looking for 'forgotten' files and adds them to
    factory.access_times so they can age and be removed like the others.

    It processes one directory entry per 'timer' seconds, which unless
    set to 0 is very slow, but it is also very light weight. And files
    which get recuested are recycled automatically anyway, so it is
    not urgent to find forgotten files. If also uses the files oun
    atime, so if the files has been there for a long time it will soon
    be removed anyway.
    
    """
    working = 0
    
    def __init__(self, factory, timer):
        self.timer = timer
        self.factory = factory
        self.callback = None
        self.working = None  # Deferred triggered when recycler finishes
    def start(self):
        """
        Starts the Recycler if it is not working, it will use
        callLater to keep working until it finishes with the whole
        tree.
        """
        if not self.working:
            self.working = defer.Deferred()
            if self.factory.backends == []:
                log.msg("NO BACKENDS FOUND",'recycle')
                self.working.errback(python.failure.Failure())
                return self.working
            self.cur_uri = '/'
            self.cur_dir = self.factory.config.cache_dir
            self.pending = []
            for backend in self.factory.backends.values():
                 self.pending.append(backend.base)
            self.stack = []
            self.callback = reactor.callLater(self.timer, self.process)
        return self.working
    def stop(self):
        if self.callback is not None:
            self.callback.cancel()
            self.callback = None
            if self.working:
                self.working.callback(None)
                self.working = None
    def pop(self):
        if self.stack:
            (self.cur_dir, self.cur_uri, self.pending) = self.stack.pop()
        else:
            self.working.callback(None)
            self.working = None
    def push(self):
        if self.pending:
            self.stack.append((self.cur_dir, self.cur_uri, self.pending))
        
    def process(self):
        """
        Process the next entry, is called automatically via callLater.
        """
        self.callback = None
        entry = self.pending.pop()
        uri  = os.path.join(self.cur_uri, entry)
        path = os.path.join(self.cur_dir, entry)
        if not os.path.exists(path):
            pass
        elif os.path.isdir(path):
            self.push()
            self.cur_dir = path
            self.cur_uri = uri
            self.pending = os.listdir(self.cur_dir)
            if not self.pending:
                # Prune directory if it has not just been created
                pathage = time.time() - os.path.getctime(path)
                if pathage > 60:
                    log.msg("Pruning empty directory: "+path,'recycle')
                    os.removedirs(path)
        elif os.path.isfile(path):
            ext = os.path.splitext(path)[1]
            if not ext == 'apDownload':
                #print "PATH:", path
                #print "URI: ", uri
                if not self.factory.access_times.has_key(uri):
                    log.msg("Adopting new file: "+ uri,'recycle')
                    self.factory.access_times[uri] = os.path.getatime(path)
        else:
            log.msg("UNKNOWN:"+path,'recycle')

        if not self.pending:
            self.pop()
        if self.working:
            self.callback = reactor.callLater(self.timer, self.process)

if __name__ == '__main__':
    #Just for testing purposes.
    from apt_proxy_conf import aptProxyFactoryConfig
    import shelve
    
    class DummyFactory:
        pass
    factory = DummyFactory()
    aptProxyFactoryConfig(factory)
    factory.access_times=shelve.open("tmp.db")
    recycle = MirrorRecycler(factory, 10)
    recycle.start()
    while recycle.working:
        recycle.process()

    factory.access_times.close()