import repository from arizona
[raven.git] / 2.0 / plush / storksyncd
1 #! /usr/bin/env python
2
3 # Jude Nelson
4 # Parts copied from pacmand
5 #
6 # Synchronizes repository metafile
7 #
8 #           [option, long option,                     variable,                      action,       data,     default,                       metavar,                       description]
9 """arizonaconfig
10     options=[
11
12             ["-C",   "--configfile",  "configfile",  "store",       "string", "/usr/local/stork/etc/stork.conf", "FILE",      "use a different config file (/usr/local/stork/etc/stork.conf is the default)"],
13              ["",    "--poll-metafile",          "pollfile",                    "store",       "string", "/usr/local/stork/var/packageinfo/stork-repository.cs.arizona.edu/packageinfo/metafile",        "pollfile",                         "location of metafile on disk"],
14              ["",    "--poll-timeout",    "timeout",    "store",   "int",    10,    None,   "Timeout (in seconds) for the system to begin polling for the metadata"],
15              ["",    "--metafile-url",  "metafile_url", "store", "string", "http://quake2.cs.arizona.edu/packageinfo/metafile", "metafile_url", "hostname and path to of the repository metafile"],
16             ["", "--sync", "do_sync", "store_true", None, False, None, "do not daemonize"],
17             ["", "--nestproxy-url", "nest_proxy_url", "store", "string", "http://localhost:6648/quake2.cs.arizona.edu/packageinfo/metafile", "nest_proxy_url", "URL to the metafile on the nest proxy"],
18             ["", "--download-protocols", "download_protocols", "store", "string", "nestproxy,urllib2,arizonatransfer", "download_protocols", "List and order of download protocols to use to fetch the file.  Options are \"arizonatransfer\", \"urllib2\", and \"nestproxy\""]
19             ]
20      includes=[]
21 """
22
23 import sys,os,signal,time
24 #should be in the same directory as the other scripts when used...
25 #sys.path += ["../python/refactor"]
26 sys.path += ["/usr/local/stork/bin"]
27 import arizonaconfig, arizonareport, arizonacomm
28 import arizonatransfer
29 import arizonageneral
30 import threading
31 import hashlib
32 import urllib2
33
34 # default values
35 glo_timeout = 10
36 glo_metafile_url = "https://stork-repository.cs.arizona.edu/packageinfo/"
37 glo_pollfile = "/tmp/storkfiles/metafile"
38 glo_nestproxy_url = "http://localhost:6480"
39 glo_download_order = ["nestproxy", "urllib2" "arizonatransfer"]
40
41 # get the metadata from the stork repository using the arizonatransfer
42 # (depricated, doesn't always work (?))
43 def get_metadata_arizonatransfer(host, dest_str, hash):
44    
45    # yoinked from CVSROOT/tools/misc/aztransfer.py
46    
47    arizonageneral.makedirs_existok("tarpackinfo")
48    arizonaconfig.set_option("tarpackinfopath", "./tarpackinfo")
49
50    hashlist = arizonaconfig.get_option("hash")
51
52    hashfuncs = []
53    if arizonaconfig.get_option("hashfuncs"):
54       for hashname in arizonaconfig.get_option("hashfuncs"):
55          if hashname == "default":
56             hashfuncs.append(arizonatransfer.default_hashfunc)
57          elif hashname == "package":
58             hashfuncs.append(storkpackage.get_package_metadata_hash)
59          else:
60             print "unknown hashfunc ", hashname
61             sys.exit(1)
62
63    filelist = []
64    dict = {'filename': os.path.basename(dest_str)}
65    if hash:
66       dict['hash'] = hash
67
68    if hashfuncs:
69       dict['hashfuncs'] = hashfuncs
70
71    filelist.append( dict )
72
73    destdir = os.path.dirname( dest_str )
74
75    arizonageneral.makedirs_existok(destdir)
76
77    # print "host = ", host
78    # print "filelist = ", filelist
79    # print "destdir = ", destdir
80
81    if filelist[0]['filename'] == "sync":
82       arizonageneral.makedirs_existok("sync")
83       (result, grabbed_list, all_list) = arizonatransfer.sync_remote_dir(host, "sync", hashfuncs=hashfuncs)
84    else:
85       (result, grabbed_list) = arizonatransfer.getfiles1(host, filelist, destdir, None, False)
86
87    # print "result = ", result
88    # print "grabbed_list = ", grabbed_list
89    return result
90
91
92 # get metadata via urllib2
93 def get_metadata_urllib2( host, dest_str ):
94    
95    try: 
96       url_opener = urllib2.build_opener()
97       metafile_page = url_opener.open( host )
98       metafile_data = metafile_page.read()
99
100       # save it...
101       destdir = os.path.dirname( dest_str )
102       arizonageneral.makedirs_existok( destdir )
103
104       fout = open( dest_str, "wt" )
105       fout.write( metafile_data )
106       fout.close()
107
108       return True
109    except:
110       print "Could not get metafile from " + host
111       pass
112
113    return False
114
115 # get metadata via stork_nest_proxy HTTP request
116 def get_metadata_nestproxy( proxy_host, dest_str, repository_host="https://stork-repository.cs.arizona.edu/packageinfo" ):
117
118    try:
119       metafile_response = urllib2.urlopen( proxy_host + "?max_age=100" )
120       
121       dest_dir = os.path.dirname( dest_str )
122       arizonageneral.makedirs_existok( dest_dir )
123
124       fout = open( dest_str, "wt" )
125       fout.write( metafile_response.read() )
126       fout.close()
127
128       return True
129
130    except:
131       print "Could not get metafile from nestproxy " + proxy_host
132       pass
133
134    return False
135
136
137 # poll the FS for a new metafile every few seconds to see if the atime has changed,
138 # and redownload it if it is too old
139 class FilePoller(threading.Thread):
140    """
141    <Purpose>
142       Initialize the thread to begin polling the FS for a new metadata file
143    """
144    def __init__(self, metadata_filename, metafile_url, nestproxy_url, download_protocol_list, timeout, verboseness):
145       threading.Thread.__init__(self)
146       self.metadata_filename = metadata_filename
147       self.nestproxy_url = nestproxy_url
148       self.download_protocol_list = download_protocol_list
149       self.timeout = time.time() + timeout    # poll every few seconds
150       self.timeout_delta = timeout
151       self.verboseness = verboseness
152       self.metafile_url = metafile_url
153
154       print "[storksyncd] will poll " + metadata_filename + " every " + str(timeout) + " seconds, and download from " + metafile_url
155       self.start()
156
157    """
158    <Purpose>
159       Poll the FS for the file matching self.metadata_filename
160       If the file exists AND has a different timestamp than
161       the currently-recognized file, then invoke pacman on it.
162    """
163    def run(self):
164       while True:
165           # are we expired?
166           if self.timeout < time.time() :
167              # new timeout...
168              self.timeout = time.time() + self.timeout_delta
169          
170              # does the file exist?  Prepare get it if not
171              atime = 0
172              if os.path.exists( self.metadata_filename ) == False :
173                 atime = -1;
174
175              else:
176                 if self.verboseness != 0:
177                    print "[storksyncd] polling for " + self.metadata_filename
178
179                 # get atime
180                 atime = os.stat(self.metadata_filename).st_mtime
181
182              # is the file too old?
183              if atime < self.timeout - 2*self.timeout_delta:
184
185                 # get the file!
186                 print "[storksyncd] metafile is too old; re-downloading..."
187
188                 # get_metadata_arizonatransfer( self.metafile_url + "metafile", self.metadata_filename, None )
189                 # get_metadata_urllib2( self.metafile_url + "metafile", self.metadata_filename )
190
191                 # attempt to get the metafile
192                 result = False
193
194                 for protocol in self.download_protocol_list:
195   
196                    if result == False and protocol == "nestproxy" and self.nestproxy_url != None:
197                       # get via nestproxy
198                       print "[storksyncd] attempting to download from nest proxy"
199                       result = get_metadata_nestproxy( self.nestproxy_url, self.metadata_filename )
200                   
201                    if result == False and protocol == "urllib2":
202                       # get via urllib2
203                       print "[storksyncd] attempting to download via urllib2"
204                       result = get_metadata_urllib2( self.metafile_url, self.metadata_filename )
205
206                    if result == False and protocol == "arizonatransfer":
207                       print "[storksyncd] attempting to download via arizonatransfer"
208                       # get via arizonatransfer
209                       result = get_metadata_arizonatransfer( self.metafile_url, self.metadata_filename, None )
210
211                    if result == True:
212                       break
213
214                 # end for
215
216                 if result == True:
217                   print "[storksyncd] saved!"
218                 else:
219                   print "[storksyncd] could not save metafile!"
220
221           else:
222             # sleep some to save CPU
223             time.sleep( self.timeout_delta )
224          # end
225       # end
226    # end
227 # end
228
229 def handler_sighup(signum, frame):
230     """
231     <Purpose>
232        Intercepts the "hangup" signal, but doesn't do anything.
233        Simply causes the sleep to return.
234     """
235     pass
236
237 def Main():
238     global glo_timeout
239     global glo_metafile_url
240     global glo_pollfile
241     global glo_nestproxy_url
242     global sync
243     global verbose
244     global pacman_update_event
245     global filepoller
246     global pollfilename
247
248     # kill any other storksyncd processes
249     procs = os.popen("ps ax | grep storksyncd")
250     proc_lines = procs.readlines()
251     procs.close()
252     for proc in proc_lines:
253        if proc.find("python") != 0:
254           pid = proc.split(" ")[0]
255           if len(pid) != 0 and str(os.getpid()) != pid:
256              os.system("sudo kill " + pid)
257
258     args = arizonaconfig.init_options("storksyncd",version="2.0", configfile_optvar="configfile")
259
260     pollfilename = arizonaconfig.get_option("pollfile")
261     metafile_url = arizonaconfig.get_option("metafile_url")
262     timeout = arizonaconfig.get_option("timeout")
263     do_sync = arizonaconfig.get_option("do_sync")
264     verbose = arizonaconfig.get_option("verbose")
265     download_protocols = arizonaconfig.get_option("download_protocols")
266     nestproxy_url = arizonaconfig.get_option("nest_proxy_url")
267
268     if pollfilename == None:
269        pollfilename = glo_pollfile
270
271     if metafile_url == None:
272        metafile_url = glo_metafile_url
273
274     if nestproxy_url == None:
275        nestproxy_url = glo_nestproxy_url
276
277     if timeout == None:
278        timeout = glo_timeout
279
280     if download_protocols == None:
281        download_protocls = "nestproxy,urllib2,arizonatransfer"
282
283     # parse the download protocols into a string
284     download_protocol_list = download_protocols.split(',')
285     for i in range(0, len(download_protocol_list) - 1):
286        download_protocol_list[i] = download_protocol_list[i].strip()
287
288     arizonareport.send_syslog( arizonareport.INFO, "[storksyncd] Will monitor \"" + pollfilename + "\" for changes")
289
290     # set the hangup signal handler
291     signal.signal(signal.SIGHUP, handler_sighup)
292
293     if do_sync == False:
294        # run as a daemon
295        arizonageneral.make_daemon("storksyncd")
296
297     # if we're supposed to poll for metadata via a file, then start doing so
298     if pollfilename != "":
299         filepoller_verbosity = 0
300         if verbose:
301             filepoller_verbosity = 1
302
303         filepoller = FilePoller(pollfilename, metafile_url, nestproxy_url, download_protocol_list, timeout, filepoller_verbosity)
304     else:
305         filepoller = None
306
307     while True:
308        time.sleep( glo_timeout / 2.0 )
309
310
311 if __name__ == "__main__":
312     Main()