6 # Copyright (c) 2003 Intel Corporation
9 # Copyright (c) 2004-2006 The Trustees of Princeton University
10 # All rights reserved.
26 # if there is no cStringIO, fall back to the original
28 from cStringIO import StringIO
30 from StringIO import StringIO
34 class BootServerRequest:
38 # all possible places to check the cdrom mount point.
39 # /mnt/cdrom is typically after the machine has come up,
40 # and /usr is when the boot cd is running
41 CDROM_MOUNT_PATH = ("/mnt/cdrom/","/usr/")
43 MONITORSERVER_CERTS= {}
50 # in seconds, how maximum time allowed for connect
51 DEFAULT_CURL_CONNECT_TIMEOUT=30
52 # in seconds, maximum time allowed for any transfer
53 DEFAULT_CURL_MAX_TRANSFER_TIME=3600
54 # location of curl executable, if pycurl isn't available
55 # and the DownloadFile method is called (backup, only
56 # really need for the boot cd environment where pycurl
61 def __init__(self, vars, verbose=0):
66 # see if we have a boot cd mounted by checking for the version file
67 # if HAS_BOOTCD == 0 then either the machine doesn't have
68 # a boot cd, or something else is mounted
71 for path in self.CDROM_MOUNT_PATH:
72 self.Message( "Checking existance of boot cd on %s" % path )
74 os.system("/bin/mount %s > /dev/null 2>&1" % path )
76 version_file= self.VARS['BOOTCD_VERSION_FILE'] % {'path' : path}
77 self.Message( "Looking for version file %s" % version_file )
79 if os.access(version_file, os.R_OK) == 0:
80 self.Message( "No boot cd found." );
82 self.Message( "Found boot cd." )
88 # check the version of the boot cd, and locate the certs
89 self.Message( "Getting boot cd version." )
91 versionRegExp= re.compile(r"PlanetLab BootCD v(\S+)")
93 bootcd_version_f= file(version_file,"r")
94 line= string.strip(bootcd_version_f.readline())
95 bootcd_version_f.close()
97 match= versionRegExp.findall(line)
99 (self.BOOTCD_VERSION)= match[0]
101 # right now, all the versions of the bootcd are supported,
102 # so no need to check it
104 self.Message( "Getting server from configuration" )
106 bootservers= [ self.VARS['BOOT_SERVER'] ]
107 for bootserver in bootservers:
108 bootserver = string.strip(bootserver)
109 cacert_path= "%s/%s/%s" % \
110 (self.VARS['SERVER_CERT_DIR'] % {'path' : path},
111 bootserver,self.VARS['CACERT_NAME'])
112 if os.access(cacert_path, os.R_OK):
113 self.BOOTSERVER_CERTS[bootserver]= cacert_path
115 monitorservers= [ self.VARS['MONITOR_SERVER'] ]
116 for monitorserver in monitorservers:
117 monitorserver = string.strip(monitorserver)
118 cacert_path= "%s/%s/%s" % \
119 (self.VARS['SERVER_CERT_DIR'] % {'path' : path},
120 monitorserver,self.VARS['CACERT_NAME'])
121 if os.access(cacert_path, os.R_OK):
122 self.MONITORSERVER_CERTS[monitorserver]= cacert_path
124 self.Message( "Set of servers to contact: %s" %
125 str(self.BOOTSERVER_CERTS) )
126 self.Message( "Set of servers to upload to: %s" %
127 str(self.MONITORSERVER_CERTS) )
129 self.Message( "Using default boot server address." )
130 self.BOOTSERVER_CERTS[self.VARS['DEFAULT_BOOT_SERVER']]= ""
131 self.MONITORSERVER_CERTS[self.VARS['DEFAULT_BOOT_SERVER']]= ""
134 def CheckProxy( self ):
135 # see if we have any proxy info from the machine
137 self.Message( "Checking existance of proxy config file..." )
139 if os.access(self.VARS['PROXY_FILE'], os.R_OK) and \
140 os.path.isfile(self.VARS['PROXY_FILE']):
141 self.PROXY= string.strip(file(self.VARS['PROXY_FILE'],'r').readline())
143 self.Message( "Using proxy %s." % self.PROXY)
145 self.Message( "Not using any proxy." )
149 def Message( self, Msg ):
155 def Error( self, Msg ):
156 sys.stderr.write( Msg + "\n" )
160 def Warning( self, Msg ):
165 def MakeRequest( self, PartialPath, GetVars,
166 PostVars, DoSSL, DoCertCheck,
167 ConnectTimeout= DEFAULT_CURL_CONNECT_TIMEOUT,
168 MaxTransferTime= DEFAULT_CURL_MAX_TRANSFER_TIME,
171 (fd, buffer_name) = tempfile.mkstemp("MakeRequest-XXXXXX")
173 buffer = open(buffer_name, "w+b")
175 # the file "buffer_name" will be deleted by DownloadFile()
177 ok = self.DownloadFile(PartialPath, GetVars, PostVars,
178 DoSSL, DoCertCheck, buffer_name,
183 # check the ok code, return the string only if it was successfull
192 # just in case it is not deleted by DownloadFile()
193 os.unlink(buffer_name)
199 def DownloadFile(self, PartialPath, GetVars, PostVars,
200 DoSSL, DoCertCheck, DestFilePath,
201 ConnectTimeout= DEFAULT_CURL_CONNECT_TIMEOUT,
202 MaxTransferTime= DEFAULT_CURL_MAX_TRANSFER_TIME,
205 self.Message( "Attempting to retrieve %s" % PartialPath )
207 # we can't do ssl and check the cert if we don't have a bootcd
208 if DoSSL and DoCertCheck and not self.HAS_BOOTCD:
209 self.Error( "No boot cd exists (needed to use -c and -s.\n" )
212 if DoSSL and not PYCURL_LOADED:
213 self.Warning( "Using SSL without pycurl will by default " \
214 "check at least standard certs." )
216 # ConnectTimeout has to be greater than 0
217 if ConnectTimeout <= 0:
218 self.Error( "Connect timeout must be greater than zero.\n" )
226 # setup the post and get vars for the request
229 postdata = urllib.urlencode(PostVars)
230 self.Message( "Posting data:\n%s\n" % postdata )
234 getstr= "?" + urllib.urlencode(GetVars)
235 self.Message( "Get data:\n%s\n" % getstr )
237 # now, attempt to make the request, starting at the first
240 cert_list = self.MONITORSERVER_CERTS
242 cert_list = self.BOOTSERVER_CERTS
244 for server in cert_list:
245 self.Message( "Contacting server %s." % server )
247 certpath = cert_list[server]
250 # output what we are going to be doing
251 self.Message( "Connect timeout is %s seconds" % \
254 self.Message( "Max transfer time is %s seconds" % \
258 url = "https://%s/%s%s" % (server,PartialPath,getstr)
260 if DoCertCheck and PYCURL_LOADED:
261 self.Message( "Using SSL version %d and verifying peer." %
262 self.CURL_SSL_VERSION )
264 self.Message( "Using SSL version %d." %
265 self.CURL_SSL_VERSION )
267 url = "http://%s/%s%s" % (server,PartialPath,getstr)
269 self.Message( "URL: %s" % url )
271 # setup a new pycurl instance, or a curl command line string
272 # if we don't have pycurl
277 # don't want curl sending any signals
278 curl.setopt(pycurl.NOSIGNAL, 1)
280 curl.setopt(pycurl.CONNECTTIMEOUT, ConnectTimeout)
281 curl.setopt(pycurl.TIMEOUT, MaxTransferTime)
283 # do not follow location when attempting to download a file
284 curl.setopt(pycurl.FOLLOWLOCATION, 0)
287 curl.setopt(pycurl.PROXY, self.PROXY )
290 curl.setopt(pycurl.SSLVERSION, self.CURL_SSL_VERSION)
293 curl.setopt(pycurl.CAINFO, certpath)
294 curl.setopt(pycurl.SSL_VERIFYPEER, 2)
297 curl.setopt(pycurl.SSL_VERIFYPEER, 0)
300 curl.setopt(pycurl.POSTFIELDS, postdata)
302 # setup multipart/form-data upload
304 curl.setopt(pycurl.HTTPPOST, FormData)
306 curl.setopt(pycurl.URL, url)
310 "--connect-timeout %d " \
312 "--header Pragma: " \
315 (self.CURL_CMD, ConnectTimeout,
316 MaxTransferTime, DestFilePath)
319 cmdline = cmdline + "--data '" + postdata + "' "
322 cmdline = cmdline + "".join(["--form '" + field + "' " for field in FormData])
325 cmdline = cmdline + "--silent "
328 cmdline = cmdline + "--proxy %s " % self.PROXY
331 cmdline = cmdline + "--sslv%d " % self.CURL_SSL_VERSION
333 cmdline = cmdline + "--cacert %s " % certpath
335 cmdline = cmdline + url
337 self.Message( "curl command: %s" % cmdline )
342 # setup the output file
343 outfile = open(DestFilePath,"wb")
345 self.Message( "Opened output file %s" % DestFilePath )
347 curl.setopt(pycurl.WRITEDATA, outfile)
349 self.Message( "Fetching..." )
351 self.Message( "Done." )
353 http_result= curl.getinfo(pycurl.HTTP_CODE)
357 self.Message( "Results saved in %s" % DestFilePath )
359 # check the code, return 1 if successfull
360 if http_result == self.HTTP_SUCCESS:
361 self.Message( "Successfull!" )
364 self.Message( "Failure, resultant http code: %d" % \
367 except pycurl.error, err:
369 self.Error( "connect to %s failed; curl error %d: '%s'\n" %
370 (server,errno,errstr) )
372 if not outfile.closed:
374 os.unlink(DestFilePath)
380 self.Message( "Fetching..." )
381 rc = os.system(cmdline)
382 self.Message( "Done." )
386 os.unlink( DestFilePath )
389 self.Message( "Failure, resultant curl code: %d" % rc )
390 self.Message( "Removed %s" % DestFilePath )
392 self.Message( "Successfull!" )
395 self.Error( "Unable to successfully contact any boot servers.\n" )
404 Usage: BootServerRequest.py [options] <partialpath>
406 -c/--checkcert Check SSL certs. Ignored if -s/--ssl missing.
407 -h/--help This help text
408 -o/--output <file> Write result to file
409 -s/--ssl Make the request over HTTPS
410 -v Makes the operation more talkative
415 if __name__ == "__main__":
418 # check out our command line options
420 opt_list, arg_list = getopt.getopt(sys.argv[1:],
422 [ "output=", "verbose", \
423 "help","ssl","checkcert"])
430 for opt, arg in opt_list:
431 if opt in ("-h","--help"):
435 if opt in ("-c","--checkcert"):
438 if opt in ("-s","--ssl"):
441 if opt in ("-o","--output"):
447 if len(arg_list) != 1:
450 partialpath= arg_list[0]
451 if string.lower(partialpath[:4]) == "http":
458 # got the command line args straightened out
459 requestor= BootServerRequest(verbose)
462 requestor.DownloadFile( partialpath, None, None, ssl,
463 checkcert, output_file)
465 result= requestor.MakeRequest( partialpath, None, None, ssl, checkcert)