import repository from arizona
[raven.git] / apps / gui2 / storkcurlfuncs.py
1 #!/usr/bin/python
2
3
4 # provides curl functions for the gui to use
5 import os
6 import sys
7 import tempfile
8 import shutil
9 import urllib
10 import re
11
12 import arizonaconfig
13 import arizonareport
14
15 # needs to be set by whatever is using this module. example: "https://stork-repository.cs.arizona.edu"
16 repository = None
17 repoport = ":8081"
18
19 scversion = "$Revision: 2.30 $"
20
21 storksite = "http://www.cs.arizona.edu/stork/"
22
23
24 #trys to log the user in
25 #   returns:
26 #     true on succussful login
27 #     false on failure
28 def login( username, password,site="www.planet-lab.org"):
29
30    print "Using site:",site
31
32    if site == "www.planet-lab.org":
33       authtype = "PLauthenticate"
34    elif site == "www.planet-lab.eu":
35       authtype = "PLEauthenticate"
36    else:
37       raise ValueError, "Unrecognized site: "+site
38
39    # the curl command to login
40    command = arizonaconfig.get_option("curlpath")+" -L -k -d \"%USERINFO%\" -D GetCookie "+repository+repoport+"/stork/login.php?"
41
42    # build the user info string
43    info = "username="+urllib.quote(username)+"&password="+urllib.quote(password)+"&authmethod="+urllib.quote(authtype)
44    command  = command.replace( "%USERINFO%", info )
45
46    arizonareport.send_out(2, "About to run: "+command)
47
48    # try to run the command and see what we get
49    (sin, sout, serr) = os.popen3( command )
50
51    outstring = sout.read()
52    errstring = serr.read()
53
54    sout.close()
55    serr.close()
56
57    if "incorrect username" not in outstring:
58       return True
59
60    else:
61       return False
62
63 # call the autosetup for the user with the specified set of nodes
64 # (this will add the nodes to the slice and will set the initscript to stork
65 def autoSetup( username, password, slice, nodes,site="www.planet-lab.org" ):
66    if site == "www.planet-lab.org":
67       authtype = "PLauthenticate"
68    elif site == "www.planet-lab.eu":
69       authtype = "PLEauthenticate"
70    else:
71       raise ValueError, "Unrecognized site: "+site
72
73    command = arizonaconfig.get_option("curlpath")+" -L -k -d \"username="+urllib.quote(username)+"&password="+urllib.quote(password)+"&slice="+urllib.quote(slice)+"&authmethod="+urllib.quote(site)+"&nodes="+urllib.quote_plus(" ".join(nodes))+"\""
74    command = command+" "+repository+repoport+"/stork/autosetup.php"
75
76    arizonareport.send_out(2, "About to run: "+command)
77
78    (sin,sout,serr) = os.popen3(command)
79    #out = sout.read()
80
81
82
83
84 def getslices():
85    """Returns an alphabetically sorted list of slices the user has access to.
86    """
87
88    #TODO this should do some error checking and return none or throw an exception if
89    # something goes wrong in retrieving the slices
90
91    command = arizonaconfig.get_option("curlpath")+" -L -k -b GetCookie "+repository+repoport+"/stork/gui_getslices.php"
92
93    arizonareport.send_out(2, "About to run: "+command)
94
95    (sin, sout, serr) = os.popen3( command )
96
97    outstring = sout.read()
98    errstring = serr.read()
99
100    sout.close()
101    serr.close()
102
103    slicelist = outstring.rstrip().rstrip("\n").split("\n")
104    slicelist.sort()
105    return slicelist
106
107
108
109 # upload a file to the repository.
110 #  the type of file being uploaded must be specified
111 #  in the type param and must be one of:
112 #     package
113 #     tp
114 #     pacman
115 #     pk 
116 #     conf
117 def upload_file(username, password, file, type, slice=None):
118    arizonareport.send_out(3, "upload_file initiated for file: "+file)
119
120    ok_types = ["package","tp","pacman","pk","conf"]
121    if type not in ok_types:
122       arizonareport.send_out(2, type+" not in "+str(ok_types)+" , skipping upload")
123       return None
124
125    if not login(username,password):
126       arizonareport.send_error(2, "ERROR: Not logged in and unable to login. Aborting file upload.")
127       return None
128
129    if not os.path.isfile(file):
130       arizonareport.send_out(2, file," is not a file, skipping upload.")
131       return None
132
133    command = arizonaconfig.get_option("curlpath")+" -L -k -b GetCookie %SLICE% -F \"type=%TYPE%\" -F \"numfiles=1\" -F \"uploadbutton=Upload File\" -F \"file_0=@%FILE%\" "+repository+repoport+"/stork/upload_handler.php"
134    command = command.replace("%FILE%", file).replace("%TYPE%", type)
135
136    if type in ["pk", "conf"]  and slice!=None:
137       command = command.replace("%SLICE%", "-F \"slice="+slice+"\"")
138    else:
139       command = command.replace("%SLICE%", "")
140    
141
142    #DEBUG -remove when we go live
143    #command  = command.replace("https://stork-repository.cs.arizona.edu", "http://jplichta.ipupdater.com:8080")
144
145    arizonareport.send_out(2, "About to run: "+command)
146
147    (sin, sout, serr) = os.popen3( command )
148
149    outstring = sout.read()
150    errstring = serr.read()
151
152    #print outstring
153
154
155
156 def get_file(relative_path,outputfile):
157    tmptuple = tempfile.mkstemp("curldownload")
158    tmp      = tmptuple[1]
159    try:
160       tmptuple[0].close()
161    except:
162       pass
163
164    command = arizonaconfig.get_option("curlpath")+" -L -w '%{http_code}' -k -o "+tmp+" "+repository+repoport+relative_path
165  
166    arizonareport.send_out(2, "About to run: "+command)
167
168    (sin, sout, serr) = os.popen3( command )
169
170    outline = sout.read()
171    sout.close()
172    serr.close()
173
174    if outline == "" or outline != "200":
175       try:
176          os.unlink(tmp)
177       except:
178          pass
179       return False
180    else:
181       try:
182          shutil.move(tmp, outputfile)
183       except:
184          pass
185       return True
186
187 def url_exists(relative_path):
188    command = arizonaconfig.get_option("curlpath")+" -L -w '%{http_code}' -k --head "+repository+":8081/"+relative_path
189  
190    arizonareport.send_out(2, "About to run: "+command)
191
192    (sin, sout, serr) = os.popen3( command )
193
194    outline = sout.read()
195    sout.close()
196    serr.close()
197
198    if outline == "" or outline != "200":
199       return False
200    else:
201       return True
202
203
204
205
206 def is_latest_version(version_string):
207    """
208       Returns a tuple (Boolean,String, String). The first part of the tuple
209       is true if this version of the gui is the latest one as reported
210       by the website (or newer), the second part of the tuple will always be None if
211       the first part is True, if the first part is False it will be a string
212       indicating the most recent version from the repository. The third part
213       of the tuple will be a string, either storkslicemanager or storkcurlfuncs
214       to indicate which file is out of date. If both are out of date, only
215       storkslicemanager will be indicated.
216    """
217    version = "gui-version"
218    command = arizonaconfig.get_option("curlpath")+" -L -w '%{http_code}' -k -o "+version+" "+storksite+"gui-version"
219    arizonareport.send_out(2, "About to run: "+command)
220
221    (sin, sout, serr) = os.popen3( command )
222    outline = sout.read()
223    sout.close()
224    serr.close()
225
226    if outline == "" or outline != "200":
227       # version page did not exist, or could not connect
228       return (False, "unknown", "unknown")
229    else:
230       # try to open the downloaded version page to check the version number
231       try:
232          f = open(version, "r")
233          # Id cvs tag of storkslicemanager should be on first line
234          line1 = f.readline().rstrip("\n").rstrip(" ")
235          line2 = f.readline().rstrip("\n").rstrip(" ")
236
237          f.close()
238  
239          current_gui_version = float(version_string.split(" ")[1])
240          current_sc_version = float(scversion.split(" ")[1])
241          available_gui_version = float(line1.split(" ")[1])
242          available_sc_version = float(line2.split(" ")[1])
243  
244          if current_gui_version >= available_gui_version and current_sc_version >= available_sc_version:
245             return (True, None, None)
246          else:
247             return (False, str(available_gui_version), "storkslicemanager")
248
249       except IOError:
250          return (False, "unknown", "unknown")          
251       except OSError:
252          return (False, "unknown", "unknown")          
253  
254 def update_gui(put_files_here):
255    command1 = arizonaconfig.get_option("curlpath")+" -L -o "+os.path.join(put_files_here,"storkslicemanager.py")+" "+storksite+"gui/storkslicemanager.py"
256    command2 = arizonaconfig.get_option("curlpath")+" -L -o "+os.path.join(put_files_here,"storkcurlfuncs.py")+" "+storksite+"gui/storkcurlfuncs.py"
257
258
259    (sin, sout, serr) = os.popen3( command1 )
260    (sin, sout, serr) = os.popen3( command2 )
261    #SHUTDOWN THE GUI
262    # TODO: open it back up for the user again
263    sys.exit(0)
264    
265
266
267
268
269 def fetch_configuration(slicename, defaultconf=False):
270    if defaultconf:
271        confurl = "http://www.cs.arizona.edu/stork/downloads/sample-stork.conf"
272    else:
273        confurl = repository+repoport+"/user-upload/conf/"+slicename+".stork.conf"
274        
275    destinationfile = slicename+".stork.conf.unsigned"
276    command = arizonaconfig.get_option("curlpath")+" -L -w '%{http_code}' -k -o "+destinationfile+" "+confurl
277  
278    arizonareport.send_out(2, "About to run: "+command)
279
280    (sin, sout, serr) = os.popen3( command )
281    outline = sout.read()
282    sout.close()
283    serr.close()
284
285    if outline == "" or outline != "200":
286       os.remove(destinationfile)
287       return False
288    else:
289       return destinationfile
290
291
292
293 def clean_configuration(conffile):
294    """
295    Perform mandatory cleaning of the configuration file for proper
296    functionality.
297    
298    """
299    writerequired = False
300    # read the file in
301    lines = []
302    try:
303       conf = open(conffile, "r")
304    except:
305       return False
306       
307    readlines = conf.readlines()
308    conf.close()
309    for line in readlines:
310       origline = line.rstrip("\n")
311       pair = read_config_line(line)
312       if not pair:
313           lines.append(origline)
314       else:
315           (directive, value) = pair
316           # comment out pacmanpackagefile
317           if directive == 'pacmanpackagefile':
318              lines.append("#pacmanpackagefile = "+value)
319              writerequired = True
320           # comment out pacmangroupfile
321           elif directive == 'pacmangroupfile':
322              lines.append("#pacmangroupfile = "+value)
323              writerequired = True
324           else:
325              lines.append(origline)
326                         
327    # now write file file back, if necessary
328    if writerequired:
329       try:
330          conf = open(conffile, "w")
331       except:
332          return False
333       for line in lines:
334          conf.write(line+"\n")
335
336    conf.close()
337    return True
338   
339   
340
341 # publickey isn't needed in there anymore
342 def alter_configuration(conffile, username, publickey, ignoreconflicts=False):
343    if not os.path.isfile(conffile): return False
344
345    hasline_requiresignedconf = False
346
347    # read the file in
348    lines = []
349    try:
350       conf = open(conffile, "r")
351    except:
352       return False
353       
354    readlines = conf.readlines()
355    conf.close()
356    for line in readlines:
357       origline = line.rstrip("\n")
358       pair = read_config_line(line)
359       if not pair:
360           if origline == 'requiresignedconf':
361              hasline_requiresignedconf = True
362           lines.append(origline)
363       else:
364           (directive, value) = pair
365           if directive == 'username':
366              curuser = value
367              if curuser != username and not ignoreconflicts:
368                 return False
369              else:
370                 lines.append("username = "+username)
371           #elif directive == "publickeyfile":
372           #   curkey = os.path.basename(value)
373           #   if curkey != publickey and not ignoreconflicts:
374           #      return False
375           #   else:
376           #      lines.append("publickeyfile = /usr/local/stork/var/keys/"+publickey)
377           else:
378                lines.append(origline)
379                         
380    #TODO detect if username or publickeyfile were completely missing and add
381    # them in if the were.
382    
383    if not hasline_requiresignedconf:
384        lines.append("requiresignedconf")
385                         
386    # now write file file back
387    try:
388       conf = open(conffile, "w")
389    except:
390       return False
391
392    for line in lines:
393       conf.write(line+"\n")
394
395    conf.close()
396    return True
397          
398          
399
400 # returns a tuple (username, pubkey)
401 def parse_config(conffile):
402    try:
403       conf = open(conffile, "r")
404    except:
405       return (None, None)
406
407    username = None
408    keyfile  = None
409
410    lines = conf.readlines()
411    for line in lines:
412       pair = read_config_line(line)
413       if pair:
414           (directive, value) = pair
415           if directive == 'username':
416              username = value
417           elif directive == "publickeyfile":
418              keyfile = os.path.basename(value)  
419
420    conf.close()
421    return (username, keyfile)
422
423
424
425 configlineregex = re.compile(r"(\S+)\s*=\s*(\S+)")
426
427 def read_config_line(line):
428    """Extract the directive and value from a config file line."""    
429    global configlineregex
430    if line.find("#") != -1:
431        line = line[:line.find("#")]
432    matches = configlineregex.match(line.strip())
433    if matches:
434        return matches.groups()
435    else:
436        return None