import repository from arizona
[raven.git] / lib / arizona-lib / planetlabAPI.py
1 # /usr/bin/env python
2
3 """
4 <Program Name>
5    planetlabAPI.py
6
7 <Started>   
8    September 16, 2007
9
10 <Author>
11    Justin Cappos
12
13 <Purpose>
14    Wraps all of the PLC authentication calls.   Hopefully I can make this less
15    of a mess than the old code for this.
16
17    Detailed information about args, the API itself, etc. is at: 
18    http://www.planet-lab.org/doc/plc_api and 
19    http://www.planet-lab.org/doc/plcapitut
20 """
21
22
23
24 import arizonaconfig
25 import xmlrpclib
26
27 # Only need this for exception handling
28 import socket
29
30 #           [option, long option,      variable,  action,        data,  default, metavar, description]
31 """arizonaconfig
32    options=[["",  "--PLauthtype",  "PLauthtype", "store",    "string","https://", None, "The protocol used for auth (default https://)"],
33             ["",  "--PLauthsite",  "PLauthsite", "store",    "string","www.planet-lab.org", None, "The authentication web site (default www.planet-lab.org)"],
34             ["",  "--PLauthport",  "PLauthport", "store",   "int", 443, None, "The port to use for authentication (default 443)"],
35             ["",  "--PLauthpath",  "PLauthpath", "store",    "string", "PLCAPI", None, "The path to use for authentication (default PLCAPI)"],
36             ["",  "--PLusername", "PLusername", "store",     "string",     None,    None, "The username for PLC actions"],
37             ["",  "--PLpassword", "PLpassword", "store",     "string",     None,    None, "The password for PLC actions"]]
38    includes=[] 
39 """
40
41
42
43
44 PLlogindata = {}
45
46
47 def PlanetLablogout():
48    """
49    <Purpose>
50       Unsets the global PLlogindata for PLC.   
51
52    <Arguments>   
53       None
54
55    <Exceptions>
56       None
57
58    <Side Effects>
59       Unsets the PLlogindata.
60
61    <Returns>
62       None
63    """
64    global PLlogindata
65    PLlogindata = {}
66    
67
68
69
70
71    
72
73
74
75
76 def PlanetLablogin(username = None, password = None, authtype = None, authsite = None, authport = None, authpath = None):
77    """
78    <Purpose>
79       Try to login to a PLC.   
80
81    <Arguments>   
82       username:   A string with the username to try 
83       password:   A string with the password to try
84       authtype:   The first part of the URL; the type of protocol
85       authsite:   The DNS name (or IP) where the data should be sent
86       authport:   The TCP port where data should be sent
87       authpath:   The path on the server (i.e. the path in the URL)
88
89    <Exceptions>
90       ValueError: This is thrown for most types of problems (bad username, bad
91                   password, etc.).   
92       I don't intentially try to pass other exceptions through.   I'm not sure
93       what xmlrpclib will raise.
94
95    <Side Effects>
96       Sets PLlogindata.   This is implicitly used throughout.
97
98    <Returns>
99       None (Exception thrown on failure)
100    """
101
102    global PLlogindata
103
104    if username == None:
105       username = arizonaconfig.get_option("PLusername") 
106       if not username:
107          raise ValueError, "Must set PlanetLab username (PLusername)"
108
109    if password == None:
110       password = arizonaconfig.get_option("PLpassword")
111       if not password:
112          raise ValueError, "Must set PlanetLab password (PLpassword)"
113
114    if password == "prompt":
115       password = raw_input("password:")
116       if not password:
117          raise ValueError, "Must set PlanetLab password (PLpassword)"
118
119    if authtype == None:
120       authtype = arizonaconfig.get_option("PLauthtype")
121    if authsite == None:
122       authsite = arizonaconfig.get_option("PLauthsite") 
123    if authport == None:
124       authport = arizonaconfig.get_option("PLauthport") 
125    if authpath == None:
126       authpath = arizonaconfig.get_option("PLauthpath")
127
128    # Build the authorization dict
129    PLlogindata['auth'] = { 'Username': username, 'AuthMethod': "password", 
130               'AuthString': password, 'Role': 'user'}
131
132    myurl = authtype+authsite+":"+str(authport)+"/"+authpath+"/"
133
134    PLlogindata['site'] = authsite
135    PLlogindata['url'] = myurl
136    
137    try:
138      PLlogindata['server'] = xmlrpclib.Server(myurl, verbose = 0, allow_none=True)
139    except IOError, errormessage:
140      # perhaps the protocol is wrong?
141      raise ValueError, errormessage
142
143
144    try:
145      PLlogindata['server'].AuthCheck(PLlogindata['auth'])
146    except xmlrpclib.Fault, errormessage:
147      errormessagestring = str(errormessage)
148      if errormessagestring.startswith("<Fault 103: ':") and errormessagestring.endswith("'>"):
149         raise ValueError, errormessagestring[14:-2]
150      else:
151         raise
152    
153    except socket.gaierror, errormessage:
154      # if I can't resolve the name of the website (for example)
155      errormessagestring = str(errormessage)
156      if errormessagestring.startswith("(7, '") and errormessagestring.endswith("')"):
157         raise ValueError, errormessagestring[5:-2]
158      elif errormessagestring.startswith("(-2, '") and errormessagestring.endswith("')"):
159         raise ValueError, errormessagestring[6:-2]
160      else:
161         raise
162
163    except socket.error, errormessage:
164      # Connection error, etc.
165      errormessagestring = str(errormessage)
166      if errormessagestring.startswith("(61, '") and errormessagestring.endswith("')"):
167         raise ValueError, errormessagestring[6:-2]
168      elif errormessagestring.startswith("(113, '") and errormessagestring.endswith("')"):
169         raise ValueError, errormessagestring[7:-2]
170      elif errormessagestring.startswith("(110, '") and errormessagestring.endswith("')"):
171         raise ValueError, errormessagestring[7:-2]
172      else:
173         raise
174
175
176    except xmlrpclib.ProtocolError, errormessage:
177      # bad path
178      raise ValueError, errormessage
179
180
181
182
183 def doplccall(commandname,*args):
184    """
185    <Purpose>
186       Perform a PLC call with *args and return the return data.   
187       THIS FUNCTION USES EVAL IN A NON-SAFE WAY (assuming malicious input)!!!
188
189    <Arguments>   
190       *args:   The arguments for the call
191       
192
193    <Exceptions>
194       ValueError: I try to throw this for most types of problems (bad 
195       PLlogindata, etc.).    I don't intentially try to pass other exceptions
196       through.   I'm not sure what xmlrpclib will raise.
197
198    <Side Effects>
199       Contacts PLC and may change the site state.
200
201    <Returns>
202       Depends on the calling function
203    """
204
205    if not PLlogindata:
206       raise ValueError, "Non-existant PLserver authentication (must log in first)"
207
208    arglist = ["PLlogindata['auth']"] 
209    for arg in args:
210       arglist.append(repr(arg))
211    
212    try:
213      retval = eval("PLlogindata['server']."+commandname+"(" + ",".join(arglist) + ")")
214    except xmlrpclib.Fault, errormessage:
215      errormessagestring = str(errormessage)
216      if errormessagestring.startswith("<Fault 100: '") and errormessagestring.endswith("'>"):
217         raise ValueError, errormessagestring[13:-2]
218      elif errormessagestring.startswith("<Fault 102: '") and errormessagestring.endswith("'>"):
219         raise ValueError, errormessagestring[13:-2]
220      else:
221         raise
222    except xmlrpclib.ProtocolError, errormessage:
223      errormessagestring = str(errormessage)
224      # xmlrpclib.ProtocolError: <ProtocolError for www.planet-lab.org:443/PLCAPI/: 500 Internal Server Error>
225      # I'll just raise this instead of trying to parse
226      raise ValueError, errormessagestring
227    except socket.error, errormessage:
228      # socket.error: (110, 'Connection timed out')
229      raise ValueError, "socket.error"+str(errormessage)
230
231    return retval
232
233
234
235
236 def getUserData():
237    """
238    <Purpose>
239       Perform a PLC call with *args and return the return data.
240       THIS FUNCTION USES EVAL IN A NON-SAFE WAY (assuming malicious input)!!!
241
242    <Arguments>
243       *args:   The arguments for the call
244
245
246    <Exceptions>
247       ValueError: I try to throw this for most types of problems (bad
248       PLlogindata, etc.).    I don't intentially try to pass other exceptions
249       through.   I'm not sure what xmlrpclib will raise.
250
251    <Side Effects>
252       Contacts PLC and may change the site state.
253
254    <Returns>
255       Depends on the calling function
256    """
257
258    global PLlogindata
259
260    if not PLlogindata:
261       raise ValueError, "Non-existant PLserver authentication (must log in first)"
262
263    try:
264      retval = PLlogindata['server'].GetPersons(PLlogindata['auth'], [PLlogindata['auth']['Username']], ['first_name', 'last_name', 'email'])
265    except xmlrpclib.Fault, errormessage:
266      errormessagestring = str(errormessage)
267      if errormessagestring.startswith("<Fault 100: '") and errormessagestring.endswith("'>"):
268         raise ValueError, errormessagestring[13:-2]
269      elif errormessagestring.startswith("<Fault 102: '") and errormessagestring.endswith("'>"):
270         raise ValueError, errormessagestring[13:-2]
271      else:
272         raise
273    except xmlrpclib.ProtocolError, errormessage:
274      errormessagestring = str(errormessage)
275      # xmlrpclib.ProtocolError: <ProtocolError for www.planet-lab.org:443/PLCAPI/: 500 Internal Server Error>
276      # I'll just raise this instead of trying to parse
277      raise ValueError, errormessagestring
278    except socket.error, errormessage:
279      # socket.error: (110, 'Connection timed out')
280      raise ValueError, "socket.error"+str(errormessagestring)
281
282    return retval