Changeset 3274

Show
Ignore:
Timestamp:
12/14/09 12:08:57 (10 years ago)
Author:
jsamuel
Message:

A handful of changes to the experimentlib. Changes include making function names match closer to the nodemanager command counterparts that underly the commands, extracting out all of the vessel status monitoring stuff into its own module because it really isn't core to the experimentlib and is instead built on top of the experimentlib, and using a helper module to do repy module imports in a way that prevents everything from repy imported into the global namespace of experimentlib (which makes it easier to write error free code and can potentially do the same for developers using this module).

Location:
seattle/trunk/experimentmanager
Files:
4 added
7 modified
1 moved

Legend:

Unmodified
Added
Removed
  • seattle/trunk/experimentmanager/examples/download_files.py

    r3267 r3274  
    5656         
    5757      # Get a list of files on the vessel. 
    58       filelist = experimentlib.get_file_list(vesselhandle, identity) 
     58      filelist = experimentlib.get_vessel_file_list(vesselhandle, identity) 
    5959       
    6060      print("Files on " + nodelocation + " " + vesselname + ": " + str(filelist)) 
  • seattle/trunk/experimentmanager/examples/get_logs.py

    r3267 r3274  
    4242      print("====================================================================") 
    4343      print("Log from " + nodelocation + " " + vesselname) 
    44       logcontents = experimentlib.get_log(vesselhandle, identity) 
     44      logcontents = experimentlib.get_vessel_log(vesselhandle, identity) 
    4545      print(logcontents) 
    4646         
  • seattle/trunk/experimentmanager/examples/lookup_node_locations.py

    r3267 r3274  
    11""" 
    2 This script will simply do an advertise lookup of a given key. 
     2This script will simply do an advertise lookup of a given key to see which 
     3nodes are advertising their location under that key. 
     4 
     5This should show the locations of all active nodes that the key has access to, 
     6whether as a user or owner of the vessel. 
    37""" 
    48 
     
    1822 
    1923 
    20 PUBLICKEY = "65537 123456789" 
     24PUBLICKEY_FILENAME = "user.publickey" 
    2125 
    2226 
     
    2630def main(): 
    2731 
    28   identity = experimentlib.create_identity_from_key_strings(PUBLICKEY) 
     32  identity = experimentlib.create_identity_from_key_files(PUBLICKEY_FILENAME) 
    2933 
    3034  nodelocation_list = experimentlib.lookup_node_locations_by_identity(identity) 
    3135 
    32   print("Number of advertising nodes: " + str(len(nodelocation_list))) 
     36  print("Number of nodes advertising their location under this key: " + str(len(nodelocation_list))) 
    3337   
    3438  for nodelocation in nodelocation_list: 
  • seattle/trunk/experimentmanager/examples/monitor_vessel_status.py

    r3268 r3274  
    2525 
    2626import experimentlib 
     27import vesselstatusmonitor 
    2728 
    2829PUBLICKEY_FILENAME = "user.publickey" 
     
    119120    # will not remove the old one and duplicates will be ignored. 
    120121    if monitorhandle is None: 
    121       monitorhandle = experimentlib.register_vessel_status_monitor(identity, vesselhandle_list, vessel_status_callback, 
    122                                                                    waittime=MONITOR_SLEEP_SECONDS) 
     122      monitorhandle = vesselstatusmonitor.register_vessel_status_monitor(identity, vesselhandle_list, vessel_status_callback, 
     123                                                                         waittime=MONITOR_SLEEP_SECONDS) 
    123124      print("Vessel status monitor registered.") 
    124125    else: 
    125       experimentlib.add_to_vessel_status_monitor(monitorhandle, vesselhandle_list)   
     126      vesselstatusmonitor.add_to_vessel_status_monitor(monitorhandle, vesselhandle_list)   
    126127 
    127128    time.sleep(LOOKUP_SLEEP_SECONDS) 
  • seattle/trunk/experimentmanager/examples/run_program.py

    r3267 r3274  
    2525# A list of the additional arguments to use when start the program on each 
    2626# vessel. This can be an empty list if no arguments are needed. 
    27 ARGUMENT_TO_START_PROGRAM_WITH = [] 
     27ARGUMENTS_TO_START_PROGRAM_WITH = [] 
    2828 
    2929 
     
    5353      # Note: you may want to reset_vessel(). 
    5454       
    55       experimentlib.upload_file(vesselhandle, identity, PROGRAM_FILE) 
     55      experimentlib.upload_file_to_vessel(vesselhandle, identity, PROGRAM_FILE) 
    5656      print("Uploaded " + PROGRAM_FILE + " to " + nodelocation + " vessel " + vesselname) 
    5757           
    58       experimentlib.start_vessel(vesselhandle, identity, PROGRAM_FILE, ARGUMENT_TO_START_PROGRAM_WITH) 
     58      experimentlib.start_vessel(vesselhandle, identity, PROGRAM_FILE, ARGUMENTS_TO_START_PROGRAM_WITH) 
    5959      print("Program started on " + nodelocation + " vessel " + vesselname) 
    6060         
  • seattle/trunk/experimentmanager/examples/seattlegeni_maintain_vessels.py

    r3268 r3274  
    115115      print("Will try to acquire " + str(num_vessels_to_request) + " vessels.") 
    116116 
    117     acquired_vessels = experimentlib.seattlegeni_acquire_vessels(identity, 'wan', num_vessels_to_request) 
     117    vessel_type = experimentlib.SEATTLEGENI_VESSEL_TYPE_WAN 
     118    acquired_vessels = experimentlib.seattlegeni_acquire_vessels(identity, vessel_type, num_vessels_to_request) 
    118119     
    119120    print("Acquired " + str(num_vessels_to_request) + " vessels: " + str(acquired_vessels)) 
  • seattle/trunk/experimentmanager/examples/upload_files.py

    r3267 r3274  
    4949       
    5050      for local_filename in FILES_TO_UPLOAD: 
    51         experimentlib.upload_file(vesselhandle, identity, local_filename) 
     51        experimentlib.upload_file_to_vessel(vesselhandle, identity, local_filename) 
    5252        print("Uploaded " + local_filename + " to " + nodelocation + " vessel " + vesselname) 
    5353       
  • seattle/trunk/experimentmanager/experimentlib.py

    r3268 r3274  
    4848 
    4949  Object Definitions: 
     50       
     51    * identity: a dictionary that minimally contains a public key but may also 
     52      contain the related private key and the username of a corresponding 
     53      SeattleGENI account. When one wants to perform any operation that would 
     54      require a public key, private key, or username, an identity must be 
     55      provided. An identity can be created using the functions named 
     56      create_identity_from_*. 
    5057   
    5158    * vesselhandle: a vesselhandle is a string that contains the information 
     
    5865       
    5966    * vesselname: a string containing the name of a vessel. This name will 
    60       be unique on any given node, but the same likely is used for vessels 
    61       on other nodes. Thus, this does not uniquely identify a vessel, in 
    62       general. To uniquely identify a vessel, a vesselhandle is needed. 
     67      be unique on any given node, but the same name is likely is used for 
     68      vessels on other nodes. Thus, this does not uniquely identify a vessel, 
     69      in general. To uniquely identify a vessel, a vesselhandle is needed. 
    6370 
    6471    * nodelocation: a string containing the location of a node. This will not 
     
    7178      the function that returns the vesseldict. See the individual function 
    7279      docstring for details. 
    73        
    74     * identity: a dictionary that minimally contains a public key but may also 
    75       contain the related private key and the username of a corresponding 
    76       SeattleGENI account. When one wants to perform any operation that would 
    77       require a public key, private key, or username, an identity must be 
    78       provided. An identity can be created using the functions named 
    79       create_identity_from_*. 
    80        
    81     * monitorhandle: an object that can be provided to various functions to 
    82       update or modify a previously created vessel status monitor. 
    8380   
    8481  Exceptions: 
     
    110107import os 
    111108import random 
    112 import sys 
    113 import threading 
     109import time 
    114110import traceback 
    115111import xmlrpclib 
     
    117113import seattlegeni_xmlrpc 
    118114 
    119 from repyportability import * 
    120 import repyhelper 
    121  
    122 repyhelper.translate_and_import("nmclient.repy") 
    123 repyhelper.translate_and_import("time.repy") 
    124 repyhelper.translate_and_import("rsa.repy") 
    125 repyhelper.translate_and_import("listops.repy") 
    126 repyhelper.translate_and_import("parallelize.repy") 
    127 repyhelper.translate_and_import("domainnameinfo.repy") 
    128 repyhelper.translate_and_import("advertise.repy")   #  used to do OpenDHT lookups 
     115# We use a helper module to do repy module imports so that we don't import 
     116# unexpected items into this module's namespace. This helps reduce errors 
     117# because editors/pylint make it clear when an unknown identifier is used 
     118# and it also makes other things easier for developers, such as using ipython's 
     119# tab completion and not causing unexpected imports if someone using this 
     120# module decides to use "from experimentlib import *" 
     121import repyimporter 
     122 
     123nmclient = repyimporter.import_repy_module("nmclient") 
     124repytime = repyimporter.import_repy_module("time") 
     125rsa = repyimporter.import_repy_module("rsa") 
     126parallelize = repyimporter.import_repy_module("parallelize") 
     127advertise = repyimporter.import_repy_module("advertise") 
    129128 
    130129# The maximum number of node locations to return from a call to lookup_node_locations. 
     
    161160SEATTLEGENI_VESSEL_TYPE_RAND = "rand" 
    162161 
    163 # TODO: Are there any other status actually used? nmstatusmonitory.py shows 
    164 # that ThreadErr and Stale may be possibilities. How about others? Also, 
    165 # we should describe what these mean. For example, what's the difference 
    166 # between stopped and terminated? 
     162# Some of these vessel status explanations are from: 
     163# https://seattle.cs.washington.edu/wiki/NodeManagerDesign 
     164 
     165# Fresh: has never been started. 
    167166VESSEL_STATUS_FRESH = "Fresh" 
     167 
     168# Started: has been started and is running when last checked. 
    168169VESSEL_STATUS_STARTED = "Started" 
     170 
     171# Stopped: was running but stopped by NM command 
    169172VESSEL_STATUS_STOPPED = "Stopped" 
     173 
     174# Stale: it last reported a start of "Started" but significant time has 
     175# elapsed, likely due to a system crash (what does "system crash" mean?). 
     176VESSEL_STATUS_STALE = "Stale" 
     177 
     178# Terminated (the vessel stopped of its own volition, possibly due to an error) 
    170179VESSEL_STATUS_TERMINATED = "Terminated" 
    171180 
     
    185194# usable/active or whether it is unusable/inactive. 
    186195VESSEL_STATUS_SET_ACTIVE = set([VESSEL_STATUS_FRESH, VESSEL_STATUS_STARTED, 
    187                                 VESSEL_STATUS_STOPPED, VESSEL_STATUS_TERMINATED]) 
     196                                VESSEL_STATUS_STOPPED, VESSEL_STATUS_STALE, 
     197                                VESSEL_STATUS_TERMINATED]) 
    188198VESSEL_STATUS_SET_INACTIVE = set([VESSEL_STATUS_NO_SUCH_NODE, VESSEL_STATUS_NO_SUCH_VESSEL, 
    189199                                  VESSEL_STATUS_NODE_UNREACHABLE]) 
     
    196206# identities/keys are being used to contact the name node. 
    197207_nmhandle_cache = {} 
    198  
    199 # Keys are monitor ids we generate. Values are dicts with the following keys: 
    200 #    'vesselhandle_list': the list of vesselhandles this monitor is registered for. 
    201 #    'vessels': stored data related to individual vessels. 
    202 #    'waittime': the number of seconds between initiating processing of vessels. 
    203 #    'callback': a the registered callback function 
    204 #    'timerhandle': a timer handle for the registered callback 
    205 #    'concurrency': the number of threads to use to process vessels. 
    206 _vessel_monitors = {} 
    207  
    208 _monitor_lock = threading.Lock() 
    209  
    210 # TODO: Do we want a global node-communication lock to prevent 
    211 # multiple threads/monitors from talking to the same node at the 
    212 # same time? The biggest concern might be just communication errors 
    213 # from more than three simultaneous connections to the node. 
    214208 
    215209# Keys are nodeids, values are nodelocations. 
     
    299293  parts = vesselhandle.split(':') 
    300294  if len(parts) != 2: 
    301     raise ValueError("vesselhandle '" + vesselhandle + "' invalid, should be nodeid:vesselname") 
     295    raise ValueError("invalid vesselhandle '" + vesselhandle + "', should be nodeid:vesselname") 
    302296 
    303297 
     
    358352 
    359353 
    360 def _debug_print(msg): 
    361   print >> sys.stderr, msg 
    362  
    363  
    364  
    365  
    366  
    367354def _initialize_time(): 
    368355  """ 
     
    382369    for localport in portlist: 
    383370      try: 
    384         time_updatetime(localport) 
     371        repytime.time_updatetime(localport) 
    385372        _initialize_time_called = True 
    386373        return 
    387       except TimeError: 
     374      except repytime.TimeError: 
    388375        error_message = traceback.format_exc() 
    389376     
     
    397384  """ 
    398385  List comprehensions are verboten by our coding style guide (generally for 
    399   good reason). Otherwise, we wouldn't have function and would just write the 
    400   following wherever needed: 
     386  good reason). Otherwise, we wouldn't have this function and would just write 
     387  the following wherever needed: 
    401388    [x[key] for x in dictlist] 
    402389  """ 
     
    435422    try: 
    436423      if 'privatekey_dict' in identity: 
    437         nmhandle = nmclient_createhandle(host, port, privatekey=identity['privatekey_dict'], 
     424        nmhandle = nmclient.nmclient_createhandle(host, port, privatekey=identity['privatekey_dict'], 
    438425                                           publickey=identity['publickey_dict'], timeout=defaulttimeout) 
    439426      else: 
    440         nmhandle = nmclient_createhandle(host, port, publickey=identity['publickey_dict'], timeout=defaulttimeout) 
    441     except NMClientException, e: 
     427        nmhandle = nmclient.nmclient_createhandle(host, port, publickey=identity['publickey_dict'], 
     428                                                  timeout=defaulttimeout) 
     429    except nmclient.NMClientException, e: 
    442430      raise NodeCommunicationError(str(e)) 
    443431     
     
    484472   
    485473  try: 
    486     phandle = parallelize_initfunction(targetlist, func, num_worker_threads, *args) 
    487    
    488     while not parallelize_isfunctionfinished(phandle): 
     474    phandle = parallelize.parallelize_initfunction(targetlist, func, num_worker_threads, *args) 
     475   
     476    while not parallelize.parallelize_isfunctionfinished(phandle): 
    489477      # TODO: Give up after a timeout? This seems risky as run_parallelized may 
    490478      # be used with functions that take a long time to complete and very large 
     
    492480      # of an assumption here. Maybe it should be an optional argument to  
    493481      # run_parallelized. 
    494       sleep(.1) 
    495      
    496     results = parallelize_getresults(phandle) 
    497   except ParallelizeError: 
     482      time.sleep(.1) 
     483     
     484    results = parallelize.parallelize_getresults(phandle) 
     485  except parallelize.ParallelizeError: 
    498486    raise SeattleExperimentError("Error occurred in run_parallelized: " +  
    499487                                 traceback.format_exc()) 
     
    535523  identity["publickey_fn"] = publickey_fn 
    536524  try: 
    537     identity["publickey_dict"] = rsa_file_to_publickey(publickey_fn) 
    538     identity["publickey_str"] = rsa_publickey_to_string(identity["publickey_dict"]) 
     525    identity["publickey_dict"] = rsa.rsa_file_to_publickey(publickey_fn) 
     526    identity["publickey_str"] = rsa.rsa_publickey_to_string(identity["publickey_dict"]) 
    539527     
    540528    if privatekey_fn is not None: 
    541529      identity["privatekey_fn"] = privatekey_fn 
    542       identity["privatekey_dict"] = rsa_file_to_privatekey(privatekey_fn) 
    543       identity["privatekey_str"] = rsa_privatekey_to_string(identity["privatekey_dict"]) 
     530      identity["privatekey_dict"] = rsa.rsa_file_to_privatekey(privatekey_fn) 
     531      identity["privatekey_str"] = rsa.rsa_privatekey_to_string(identity["privatekey_dict"]) 
    544532  except IOError: 
    545533    raise 
     
    580568  identity["username"] = username 
    581569  try: 
    582     identity["publickey_dict"] = rsa_string_to_publickey(publickey_string) 
    583     identity["publickey_str"] = rsa_publickey_to_string(identity["publickey_dict"]) 
     570    identity["publickey_dict"] = rsa.rsa_string_to_publickey(publickey_string) 
     571    identity["publickey_str"] = rsa.rsa_publickey_to_string(identity["publickey_dict"]) 
    584572     
    585573    if privatekey_string is not None: 
    586       identity["privatekey_dict"] = rsa_string_to_privatekey(privatekey_string) 
    587       identity["privatekey_str"] = rsa_privatekey_to_string(identity["privatekey_dict"]) 
     574      identity["privatekey_dict"] = rsa.rsa_string_to_privatekey(privatekey_string) 
     575      identity["privatekey_str"] = rsa.rsa_privatekey_to_string(identity["privatekey_dict"]) 
    588576  except IOError: 
     577    # Raised if there is a problem reading the file. 
    589578    raise 
    590579  except ValueError: 
     580    # Raised by the repy rsa module when the key is invald. 
    591581    raise 
    592582 
     
    600590  """Does the actual work of an advertise lookup.""" 
    601591   
    602   keydict = rsa_string_to_publickey(keystring) 
     592  keydict = rsa.rsa_string_to_publickey(keystring) 
    603593  try: 
    604594    if lookuptype is not None: 
    605       nodelist = advertise_lookup(keydict, maxvals=max_lookup_results, timeout=defaulttimeout, lookuptype=lookuptype) 
     595      nodelist = advertise.advertise_lookup(keydict, maxvals=max_lookup_results, timeout=defaulttimeout, lookuptype=lookuptype) 
    606596    else: 
    607       nodelist = advertise_lookup(keydict, maxvals=max_lookup_results, timeout=defaulttimeout) 
    608   except AdvertiseError, e: 
     597      nodelist = advertise.advertise_lookup(keydict, maxvals=max_lookup_results, timeout=defaulttimeout) 
     598  except advertise.AdvertiseError, e: 
    609599    raise UnableToPerformLookupError("Failure when trying to perform advertise lookup: " +  
    610600                                     traceback.format_exc()) 
     
    728718    nmhandle = _get_nmhandle(nodelocation, identity) 
    729719    try: 
    730       nodeinfo = nmclient_getvesseldict(nmhandle) 
    731     except NMClientException, e: 
     720      nodeinfo = nmclient.nmclient_getvesseldict(nmhandle) 
     721    except nmclient.NMClientException, e: 
    732722      raise NodeCommunicationError("Failed to communicate with node " + nodelocation + ": " + str(e)) 
    733723   
     
    743733        usablevessels.append(vesselname) 
    744734   
    745     nodeid = rsa_publickey_to_string(nodeinfo['nodekey']) 
     735    nodeid = rsa.rsa_publickey_to_string(nodeinfo['nodekey']) 
    746736    # For efficiency, let's update the _node_location_cache with this info. 
    747737    # This can prevent individual advertise lookups of each nodeid by other 
     
    774764 
    775765 
    776 def _run_monitor(monitordict): 
    777   """Performs the actual monitoring of vessels.""" 
    778   # Copy the vesselhandle_list so that changes made to the list can't happen 
    779   # while the parallelized call is being done. 
    780   vesselhandle_list = monitordict['vesselhandle_list'][:] 
    781    
    782   run_parallelized(vesselhandle_list, _check_vessel_status_change, monitordict) 
    783    
    784   # We finished the last run, now schedule another. 
    785   monitordict['timerhandle'] = settimer(monitordict['waittime'], _run_monitor, [monitordict]) 
    786  
    787  
    788  
    789  
    790  
    791 def _check_vessel_status_change(vesselhandle, monitordict): 
    792   """ 
    793   Checks the status of an individual vessel and calls the registered 
    794   callback function for the monitor if the vessel's status has changed since 
    795   the last time it was checked. 
    796   """ 
    797   try: 
    798     # When the monitor is removed/canceled, the parallelized function isn't 
    799     # aborted and we instead just have each of these calls immediately return. 
    800     if monitordict['canceled']: 
    801       return 
    802      
    803     datadict = monitordict['vessels'][vesselhandle] 
    804     if 'status' not in datadict: 
    805       datadict['status'] = '' 
    806        
    807     old_data = datadict.copy() 
    808      
    809     status = get_vessel_status(vesselhandle, monitordict['identity']) 
    810     datadict['status'] = status 
    811      
    812     # No matter where the above try block returned from, we want to see if 
    813     # the vessel data changed and call the user's callback if it has. 
    814     new_data = datadict.copy() 
    815      
    816     # Note that by not letting the lock go before we call the user's callback 
    817     # function, the processing of all of the vessels will slow down but we 
    818     # avoid requiring the user to handle locking to protect against another 
    819     # call to the callback for the same vessel. 
    820     if old_data['status'] != new_data['status']: 
    821       try: 
    822         # TODO: make sure that exception's from the user's code end up 
    823         # somewhere where the user has access to them. For now, we leave it to 
    824         # the user to make sure they handle exceptions rather than let them 
    825         # escape their callback and this is documented in the docstring of 
    826         # the function register_vessel_status_monitor. 
    827         monitordict['callback'](vesselhandle, old_data['status'], new_data['status']) 
    828        
    829       except Exception: 
    830         _debug_print("Exception occurred in vessel status change callback:") 
    831         _debug_print(traceback.format_exc()) 
    832    
    833     # In order to prevent repeating failures, we remove the vesselhandle 
    834     # from the monitor's list if the status indicates a positive response. 
    835     # This means that scripts should occasionally add their known active 
    836     # vessels to the monitor to prevent temporary failures from causing the 
    837     # vessel to be subsequently ignored forever. 
    838     if status in VESSEL_STATUS_SET_INACTIVE: 
    839       _monitor_lock.acquire() 
    840       try: 
    841         monitordict['vesselhandle_list'].remove(vesselhandle) 
    842         # We don't "del monitordict['vessels'][vesselhandle]" because it 
    843         # doesn't hurt anything to leave it other than taking up a bit of 
    844         # space, and it feels safer to leave it there just in case, for 
    845         # example, this code got changed to put the "remove" call in the 
    846         # try block above when access to the vessel's lock is still needed. 
    847       finally: 
    848         _monitor_lock.release() 
    849        
    850   except Exception: 
    851     _debug_print(traceback.format_exc()) 
    852  
    853  
    854  
    855  
    856  
    857  
    858 def register_vessel_status_monitor(identity, vesselhandle_list, callback, waittime=300, concurrency=10): 
    859   """ 
    860   <Purpose> 
    861     Registers a vessel status monitor. Once registered, a monitor occassionally 
    862     checks the status of each vessel. If the vessel's status has changed or was 
    863     never checked before, the provided callback function is called with 
    864     information about the status change. 
    865   <Arguments> 
    866     identity 
    867       The identity to be used when looking checking vessel status. This is 
    868       mostly needed to determine whether the vessel exists but no longer is 
    869       usable by the identity (that is, if the public key of the identity is 
    870       no longer neither the owner or a user of the vessel). 
    871     vesselhandle_list 
    872       A list of vesselhandles of the vessels to be monitored. 
    873     callback 
    874       The callback function. This should accept three arguments: 
    875         (vesselhandle, oldstatus, newstatus) 
    876       where oldstatus and newstatus are both strings. Any exceptions raised by 
    877       the callback will be silently ignored, so the callback should implement 
    878       exception handling. 
    879     waittime 
    880       How many seconds to wait between status checks. This will be the time 
    881       between finishing a check of all vessels and starting another round of 
    882       checking. 
    883     concurrency 
    884       The number of threads to use for communicating with nodes. This will be 
    885       the maximum number of vessels that can be checked simultaneously. 
    886   <Exceptions> 
    887     None 
    888   <Side Effects> 
    889     Immediately starts a vessel monitor running. 
    890   <Returns> 
    891     A monitorhandle which can be used to update or cancel this monitor. 
    892   """ 
    893   _validate_vesselhandle_list(vesselhandle_list) 
    894   _validate_identity(identity) 
    895    
    896   # We copy the vesselhandle_list so that the user doesn't directly modify. 
    897   vesselhandle_list = vesselhandle_list[:] 
    898    
    899   _monitor_lock.acquire() 
    900   try: 
    901     # Create a new monitor key in the the _vessel_monitors dict. 
    902     for attempt in range(10): 
    903       id = "MONITOR_" + str(random.random()) 
    904       if id not in _vessel_monitors: 
    905         break 
    906     else: 
    907       # I don't intend users to need to worry about this exception. I also 
    908       # don't know of a more specific built-in exception to use and I don't 
    909       # feel this should raise a SeattleExperimentException. Therefore, 
    910       # intentionally raising a generic Exception here. 
    911       raise Exception("Can't generate a unique vessel monitor id. " +  
    912                       "This probably means a bug in experimentlib.py") 
    913     _vessel_monitors[id] = {} 
    914      
    915     _vessel_monitors[id]['vesselhandle_list'] = vesselhandle_list 
    916     _vessel_monitors[id]['waittime'] = waittime 
    917     _vessel_monitors[id]['callback'] = callback 
    918     _vessel_monitors[id]['concurrency'] = concurrency 
    919     _vessel_monitors[id]['identity'] = identity 
    920     # Whether the monitor was canceled/removed. Used to indicate to a running 
    921     # monitor that it should stop doing work. 
    922     _vessel_monitors[id]['canceled'] = False 
    923      
    924     # Keeps track of the status of individual vessels. This is used by 
    925     # vessel monitors to determine when the status has changed. 
    926     _vessel_monitors[id]['vessels'] = {} 
    927     for handle in vesselhandle_list: 
    928       _vessel_monitors[id]['vessels'][handle] = {} 
    929      
    930     # The first time we run it we don't delay. Storing the timer handle is a 
    931     # bit useless in this case but we do it for consistency. 
    932     _vessel_monitors[id]['timerhandle'] = settimer(0, _run_monitor, [_vessel_monitors[id]]) 
    933      
    934     return id 
    935    
    936   finally: 
    937     _monitor_lock.release() 
    938  
    939    
    940  
    941  
    942  
    943 def remove_vessel_status_monitor(monitorhandle): 
    944   """ 
    945   <Purpose> 
    946     Cancel a monitor that was created through register_vessel_status_monitor. 
    947     Note that this will not terminate any already active run of the monitor 
    948     (a run is a pass through contacting all relevant nodes to determine 
    949     vessel status), but it will prevent future runs from starting. 
    950   <Arguments> 
    951     monitorhandle 
    952       A monitorhandle returned by register_vessel_status_monitor. 
    953   <Exceptions> 
    954     ValueError 
    955       If no such monitorhandle exists (including if it was already removed). 
    956   <Side Effects> 
    957     Stops future runs of the monitor and signals to any currently running 
    958     monitor to stop. It is still possible that the registered callback for 
    959     the monitor will be called after this function returns. 
    960   <Returns> 
    961     None 
    962   """ 
    963   _monitor_lock.acquire() 
    964   try: 
    965     # Ensure the monitorhandle is valid. 
    966     if not monitorhandle in _vessel_monitors: 
    967       raise ValueError("The provided monitorhandle is invalid: " + str(monitorhandle)) 
    968      
    969     # Not using parallelize_abortfunction() because I didn't want to complicate 
    970     # things by needing a way to get a hold of the parellelizehandle. Instead, 
    971     # individual calls to _check_vessel_status_change will check if the monitor 
    972     # was removed/canceled before doing any work. 
    973     _vessel_monitors[monitorhandle]['canceled'] = True 
    974      
    975     # Ignore the return value from canceltimer. If the user wants to ensure 
    976     # that no further actions are taken in their own code due to this monitor, 
    977     # they can do so by ignoring calls to their provided callback. 
    978     canceltimer(_vessel_monitors[monitorhandle]['timerhandle']) 
    979      
    980     del _vessel_monitors[monitorhandle] 
    981      
    982   finally: 
    983     _monitor_lock.release() 
    984  
    985  
    986  
    987  
    988  
    989 def add_to_vessel_status_monitor(monitorhandle, vesselhandle_list): 
    990   """ 
    991   <Purpose> 
    992     Adds the vesselhandles in vesselhandle_list to the specified monitor. If 
    993     any already are watched by the monitor, they are silently ignored. There 
    994     is no removal of previously added vesselhandles other than automatic 
    995     removal done when vessels are unreachable or otherwise invalid. 
    996      
    997     One intention of this function is that new vessels found via a 
    998     lookup_node_locations_by_identity and then find_vessels_on_nodes can be 
    999     passed to this function as a way of making sure the monitor knows about 
    1000     new vessels that a user has just obtained access to or which have recently 
    1001     come online.  
    1002   <Arguments> 
    1003     monitorhandle 
    1004       A monitorhandle returned by register_vessel_status_monitor. 
    1005     vesselhandle_list 
    1006       A list of vesselhandles to add to the monitor. 
    1007   <Side Effects> 
    1008     The next run of the monitor will include the provided vesselhandles. 
    1009   <Exceptions> 
    1010     ValueError 
    1011       If no such monitorhandle exists (including if it was already removed). 
    1012   <Returns> 
    1013     None 
    1014   """ 
    1015   _validate_vesselhandle_list(vesselhandle_list) 
    1016    
    1017   _monitor_lock.acquire() 
    1018   try: 
    1019     # Ensure the monitorhandle is valid. 
    1020     if not monitorhandle in _vessel_monitors: 
    1021       raise ValueError("The provided monitorhandle is invalid: " + str(monitorhandle)) 
    1022      
    1023     for vesselhandle in vesselhandle_list: 
    1024       if vesselhandle not in _vessel_monitors[monitorhandle]['vesselhandle_list']: 
    1025         _vessel_monitors[monitorhandle]['vesselhandle_list'].append(vesselhandle) 
    1026         _vessel_monitors[monitorhandle]['vessels'][vesselhandle] = {} 
    1027      
    1028   finally: 
    1029     _monitor_lock.release() 
    1030  
    1031  
    1032  
    1033  
    1034  
    1035766def get_vessel_status(vesselhandle, identity): 
    1036767  """ 
     
    1060791    nodelocation = get_node_location(nodeid) 
    1061792  except NodeLocationNotAdvertisedError, e: 
    1062     # If we can't find the node, then it must not be advertising. 
    1063     _debug_print("get_vessel_status cannot determine location of node: " + str(e)) 
    1064793    return VESSEL_STATUS_NO_SUCH_NODE 
    1065794   
     
    1103832   
    1104833  try: 
    1105     return nmclient_rawsay(nmhandle, requestname, *args) 
    1106   except NMClientException, e: 
     834    return nmclient.nmclient_rawsay(nmhandle, requestname, *args) 
     835  except nmclient.NMClientException, e: 
    1107836    raise NodeCommunicationError(str(e)) 
    1108837 
     
    1119848   
    1120849  try: 
    1121     return nmclient_signedsay(nmhandle, requestname, vesselname, *args) 
    1122   except NMClientException, e: 
     850    return nmclient.nmclient_signedsay(nmhandle, requestname, vesselname, *args) 
     851  except nmclient.NMClientException, e: 
    1123852    raise NodeCommunicationError(str(e)) 
    1124853 
     
    1127856 
    1128857 
    1129 def get_offcut_resources(nodeid): 
     858def get_node_offcut_resources(nodeid): 
    1130859  """ 
    1131860  <Purpose> 
     
    1144873    A string containing information about the node's offcut resources. 
    1145874  """ 
    1146   # TODO: This function might be more useful to process the string returned 
    1147   # by the nodemanager and return it from this function as some well-defined 
    1148   # data structure. 
     875  # TODO: This function might be more useful if it processed the string 
     876  # returned by the nodemanager and return it from this function as some 
     877  # well-defined data structure. 
    1149878  return _do_public_node_request(nodeid, "GetOffcutResources") 
    1150879   
     
    1153882 
    1154883 
    1155 def get_restrictions_info(vesselhandle): 
     884def get_vessel_resources(vesselhandle): 
    1156885  """ 
    1157886  <Purpose> 
     
    1171900    A string containing the vessel resource/restrictions information. 
    1172901  """ 
    1173   # TODO: This function might be more useful to process the string returned 
    1174   # by the nodemanager and return it from this function as some well-defined 
    1175   # data structure. 
     902  # TODO: This function might be more useful if it processed the string 
     903  # returned by the nodemanager and return it from this function as some 
     904  # well-defined data structure. 
    1176905  nodeid, vesselname = get_nodeid_and_vesselname(vesselhandle) 
    1177906  return _do_public_node_request(nodeid, "GetVesselResources", vesselhandle) 
     
    1181910 
    1182911 
    1183 def get_log(vesselhandle, identity): 
     912def get_vessel_log(vesselhandle, identity): 
    1184913  """ 
    1185914  <Purpose> 
     
    1206935 
    1207936 
    1208 def get_file_list(vesselhandle, identity): 
     937 
     938def get_vessel_file_list(vesselhandle, identity): 
    1209939  """ 
    1210940  <Purpose> 
     
    1227957  _validate_vesselhandle(vesselhandle) 
    1228958  file_list_string = _do_signed_vessel_request(identity, vesselhandle, "ListFilesInVessel") 
    1229   return file_list_string.split(' ') 
    1230  
    1231  
    1232  
    1233  
    1234 def upload_file(vesselhandle, identity, local_filename, remote_filename=None): 
     959  if not file_list_string: 
     960    return [] 
     961  else: 
     962    return file_list_string.split(' ') 
     963 
     964 
     965 
     966 
     967 
     968def upload_file_to_vessel(vesselhandle, identity, local_filename, remote_filename=None): 
    1235969  """ 
    1236970  <Purpose> 
     
    12751009 
    12761010 
    1277 def download_file(vesselhandle, identity, remote_filename, local_filename=None, 
    1278                   add_location_suffix=False, return_file_contents=False): 
     1011def download_file_from_vessel(vesselhandle, identity, remote_filename, local_filename=None, 
     1012                              add_location_suffix=False, return_file_contents=False): 
    12791013  """ 
    12801014  <Purpose> 
     
    13371071 
    13381072 
    1339 def delete_file(vesselhandle, identity, filename): 
     1073def delete_file_in_vessel(vesselhandle, identity, filename): 
    13401074  """ 
    13411075  <Purpose> 
     
    14951229 
    14961230 
    1497 def combine_vessels(identity, vesselhandle1, vesselhandle2): 
    1498   """ 
    1499   <Purpose> 
    1500     Join a two vessels on the same node into one, larger vessel. 
     1231def join_vessels(identity, vesselhandle1, vesselhandle2): 
     1232  """ 
     1233  <Purpose> 
     1234    Join (combine) two vessels on the same node into one, larger vessel. 
    15011235     
    15021236    THIS OPERATION IS ONLY AVAILABLE TO THE OWNER OF THE VESSEL. 
     
    17581492          # We create an nmhandle directly because we want to use it to test 
    17591493          # basic communication, which is done when an nmhandle is created. 
    1760           nmhandle = nmclient_createhandle(host, int(portstr)) 
    1761         except NMClientException, e: 
     1494          nmhandle = nmclient.nmclient_createhandle(host, int(portstr)) 
     1495        except nmclient.NMClientException, e: 
    17621496          continue 
    17631497        else: 
    1764           nmclient_destroyhandle(nmhandle) 
     1498          nmclient.nmclient_destroyhandle(nmhandle) 
    17651499          _node_location_cache[nodeid] = possiblelocation 
    17661500          break 
     
    17761510 
    17771511 
    1778  
    1779    
    17801512   
    17811513def _call_seattlegeni_func(func, *args, **kwargs): 
     
    18051537  if "seattlegeniclient" not in identity: 
    18061538    _validate_identity(identity, require_private_key=True, require_username=True) 
    1807     private_key_string = rsa_privatekey_to_string(identity["privatekey_dict"]) 
     1539    private_key_string = rsa.rsa_privatekey_to_string(identity["privatekey_dict"]) 
    18081540    # We use _call_seattlegeni_func because the SeattleGENIClient constructor 
    18091541    # may attempt to communicate with SeattleGENI. 
     
    18381570 
    18391571 
    1840 def seattlegeni_acquire_vessels(identity, type, number): 
     1572def seattlegeni_acquire_vessels(identity, vesseltype, number): 
    18411573  """ 
    18421574  <Purpose> 
     
    18471579    identity 
    18481580      The identity to use for communicating with SeattleGENI. 
    1849     type 
     1581    vesseltype 
    18501582      The type of vessels to be acquired. This must be one of the constants 
    18511583      named SEATTLEGENI_VESSEL_TYPE_* 
     
    18631595  """ 
    18641596  client = _get_seattlegeni_client(identity) 
    1865   seattlegeni_vessel_list = _call_seattlegeni_func(client.acquire_resources, type, number) 
     1597  seattlegeni_vessel_list = _call_seattlegeni_func(client.acquire_resources, vesseltype, number) 
    18661598 
    18671599  _seattlegeni_cache_node_locations(seattlegeni_vessel_list) 
     
    20561788  """   
    20571789  client = _get_seattlegeni_client(identity) 
     1790  # We can't cache this value because it may change as the user's donations 
     1791  # come online and go offline. 
    20581792  return _call_seattlegeni_func(client.get_account_info)['max_vessels'] 
    20591793 
     
    20781812  """   
    20791813  client = _get_seattlegeni_client(identity) 
    2080   return _call_seattlegeni_func(client.get_account_info)['user_port'] 
     1814  # The user port won't change, so let's not make a new seattlegeni request 
     1815  # each time just in case someone uses this a lot in their program. We'll go 
     1816  # ahead and keep in this in the identity. It's not a documented part of 
     1817  # the identity so nobody should be trying to access it directly. 
     1818  if 'user_port' not in identity: 
     1819    identity['user_port'] = _call_seattlegeni_func(client.get_account_info)['user_port'] 
     1820  return identity['user_port']