Changeset 5718

Show
Ignore:
Timestamp:
08/30/12 14:45:06 (7 years ago)
Author:
mkaplan
Message:

Repy now uses optparse for command line argument parsing (isntead of getopt), for clarity

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • seattle/branches/repy_v2/repy/repy.py

    r5608 r5718  
    55  Brent Couvrette   (2/27/09) -- added servicelog commandline option 
    66  Conrad Meyer (5/22/09) -- switch option parsing to getopt 
     7  Moshe Kaplan (8/15/12) -- switched option parsing to optparse 
    78 
    89<Start Date> 
     
    4041 
    4142 
    42 # Let's make sure the version of python is supported 
     43 
     44import os 
     45import sys 
     46import time 
     47import optparse 
     48import threading 
     49 
     50# Relative imports 
     51 
     52# First make sure the version of python is supported 
    4353import checkpythonversion 
    4454checkpythonversion.ensure_python_version_is_supported() 
    4555 
     56import safe 
     57import nanny 
     58import emulcomm 
    4659import idhelper 
    47 import safe 
    48 import sys 
    49 import getopt 
    50 import emulcomm 
     60import harshexit 
    5161import namespace 
    52 import nanny 
    53 import time 
    54 import threading 
     62import nonportable 
    5563import loggingrepy 
    56  
     64import statusstorage 
     65import repy_constants 
    5766import nmstatusinterface 
    58  
    59 import harshexit 
    60  
    61 import statusstorage 
    62  
    63 import repy_constants    
    64  
    65 import os 
    6667 
    6768# Armon: Using VirtualNamespace as an abstraction around direct execution 
     
    7172import tracebackrepy 
    7273 
    73 import nonportable 
    74  
    7574from exception_hierarchy import * 
    7675 
    7776# BAD: REMOVE these imports after we remove the API calls 
    78 import emulfile 
     77#import emulfile 
    7978import emulmisc 
    80 import emultimer 
     79#import emultimer 
    8180 
    8281# Disables safe, and resumes normal fork 
     
    171170  return 
    172171 
    173  
    174  
    175 def usage(str_err=""): 
    176   # Ivan 12/24/2008 
    177   """ 
    178    <Purpose> 
    179       Prints repy.py usage and possibly an error supplied argument 
    180    <Arguments> 
    181       str_err (string): 
    182         Options error to print to stdout 
    183    <Exceptions> 
    184       None 
    185    <Side Effects> 
    186       Modifies stdout 
    187    <Returns> 
    188       None 
    189   """ 
    190   print 
    191   if str_err: 
    192     print "Error:", str_err 
    193   print """ 
    194 Usage: repy.py [options] resourcefile program_to_run.repy [program args] 
    195  
    196 Where [options] are some combination of the following: 
    197  
    198 --ip IP                : This flag informs Repy that it is allowed to bind to the given given IP. 
    199                        : This flag may be asserted multiple times. 
    200                        : Repy will attempt to use IP's and interfaces in the order they are given. 
    201 --iface interface      : This flag informs Repy that it is allowed to bind to the given interface. 
    202                        : This flag may be asserted multiple times. 
    203 --nootherips           : Instructs Repy to only use IP's and interfaces that are explicitly given. 
    204                        : It should be noted that loopback (127.0.0.1) is always permitted. 
    205 --logfile filename.txt : Set up a circular log buffer and output to logfilename.txt 
    206 --stop filename        : Repy will watch for the creation of this file and abort when it happens 
    207                        : File can have format EXITCODE;EXITMESG. Code 44 is Stopped and is the default. 
    208                        : EXITMESG will be printed prior to exiting if it is non-null. 
    209 --status filename.txt  : Write status information into this file 
    210 --cwd dir              : Set Current working directory 
    211 --servicelog           : Enable usage of the servicelogger for internal errors 
    212 """ 
    213   return 
    214  
    215  
    216172def init_repy_location(repy_directory): 
    217  
    218173   
    219174  # Translate into an absolute path 
     
    243198 
    244199 
    245  
    246    
    247 def init_commandline_options(args): 
    248  
    249   # let's get the arguments! 
    250  
    251   try: 
    252     optlist, extraargs = getopt.getopt(args, '', [ 
    253       'ip=', 'iface=', 'nootherips', 'logfile=', 
    254       'stop=', 'status=', 'cwd=', 'servicelog' 
    255       ]) 
    256  
    257   except getopt.GetoptError: 
    258     usage() 
    259     sys.exit(1) 
    260  
    261   # By default we don't want to use the service logger 
    262   servicelog = False 
    263  
    264   # Default logfile (if the option --logfile isn't passed) 
    265   logfile = None 
    266  
    267   # Default stopfile (if the option --stopfile isn't passed) 
    268   stopfile = None 
    269  
    270   # Default stopfile (if the option --stopfile isn't passed) 
    271   statusfile = None 
    272  
    273   if len(extraargs) < 2: 
    274     usage("Must supply a resource file and a program file to execute") 
    275     sys.exit(1) 
    276  
    277   for option, value in optlist: 
    278     if option == '--ip': 
    279       emulcomm.user_ip_interface_preferences = True 
    280  
    281       # Append this ip to the list of available ones if it is new, since 
    282       # multiple IP's may be specified 
    283       if (True, value) not in emulcomm.user_specified_ip_interface_list: 
    284         emulcomm.user_specified_ip_interface_list.append((True, value)) 
    285  
    286     elif option == '--iface': 
    287       emulcomm.user_ip_interface_preferences = True 
     200def add_repy_options(parser): 
     201  """Adds the Repy command-line options to the specified optparser 
     202  """ 
     203 
     204  parser.add_option('--ip', 
     205                    action="append", type="string", dest="ip" , 
     206                    help="Explicitly allow Repy to bind to the specified IP. This option can be used multiple times." 
     207                    ) 
     208  parser.add_option('--iface', 
     209                    action="append", type="string", dest="interface", 
     210                    help="Explicitly allow Repy to bind to the specified interface. This option can be used multiple times." 
     211                    ) 
     212  parser.add_option('--nootherips', 
     213                    action="store_true", dest="nootherips",default=False, 
     214                    help="Do not allow IPs or interfaces that are not explicitly specified" 
     215                    ) 
     216  parser.add_option('--logfile', 
     217                    action="store", type="string", dest="logfile", 
     218                    help="Set up a circular log buffer and output to logfile" 
     219                    ) 
     220  parser.add_option('--stop', 
     221                    action="store", type="string", dest="stopfile", 
     222                    help="Watch for the creation of stopfile and abort when it is created" 
     223                    ) 
     224  parser.add_option('--status', 
     225                    action="store", type="string", dest="statusfile", 
     226                    help="Write status information into statusfile" 
     227                    ) 
     228  parser.add_option('--cwd', 
     229                    action="store", type="string", dest="cwd", 
     230                    help="Set Current working directory to cwd" 
     231                    ) 
     232  parser.add_option('--servicelog', 
     233                    action="store_true", dest="servicelog", 
     234                    help="Enable usage of the servicelogger for internal errors" 
     235                    ) 
     236     
     237def parse_options(options): 
     238  """ Parse the specified options and initialize all required structures 
     239  Note: This modifies global state, specifically, the emulcomm module 
     240  """ 
     241  if options.ip: 
     242    emulcomm.user_ip_interface_preferences = True 
     243 
     244    # Append this ip to the list of available ones if it is new 
     245    for ip in options.ip: 
     246      if (True, ip) not in emulcomm.user_specified_ip_interface_list: 
     247        emulcomm.user_specified_ip_interface_list.append((True, ip)) 
     248 
     249  if options.interface: 
     250    emulcomm.user_ip_interface_preferences = True 
    288251       
    289       # Append this interface to the list of available ones if it is new 
    290       if (False, value) not in emulcomm.user_specified_ip_interface_list: 
    291         emulcomm.user_specified_ip_interface_list.append((False, value)) 
    292  
    293     # Check if they have told us explicitly not to allow other IP's 
    294     elif option == '--nootherips': 
    295       # Set user preference to True 
    296       emulcomm.user_ip_interface_preferences = True 
    297       # Disable nonspecified IP's 
    298       emulcomm.allow_nonspecified_ips = False 
     252    # Append this interface to the list of available ones if it is new 
     253    for interface in options.interface: 
     254      if (False, interface) not in emulcomm.user_specified_ip_interface_list: 
     255        emulcomm.user_specified_ip_interface_list.append((False, interface)) 
     256 
     257  # Check if they have told us to only use explicitly allowed IP's and interfaces 
     258  if options.nootherips: 
     259    # Set user preference to True 
     260    emulcomm.user_ip_interface_preferences = True 
     261    # Disable nonspecified IP's 
     262    emulcomm.allow_nonspecified_ips = False 
    299263     
    300     elif option == '--logfile': 
    301       # set up the circular log buffer... 
    302       logfile = value 
    303  
    304     elif option == '--stop': 
    305       # Watch for the creation of this file and abort when it happens... 
    306       stopfile = value 
    307  
    308     elif option == '--status': 
    309       # Write status information into this file... 
    310       statusfile = value 
    311  
    312     # Set Current Working Directory 
    313     elif option == '--cwd': 
    314       os.chdir(value) 
    315  
    316     # Enable logging of internal errors to the service logger. 
    317     elif option == '--servicelog': 
    318       servicelog = True 
    319  
    320   # Update repy current directory 
    321   repy_constants.REPY_CURRENT_DIR = os.path.abspath(os.getcwd()) 
    322  
    323   # Initialize the NM status interface 
    324   nmstatusinterface.init(stopfile, statusfile) 
    325    
    326   # Write out our initial status 
    327   statusstorage.write_status("Started") 
    328  
    329  
    330   # We also need to pass in whether or not we are going to be using the service 
    331   # log for repy.  We provide the repy directory so that the vessel information 
    332   # can be found regardless of where we are called from... 
    333   tracebackrepy.initialize(servicelog, repy_constants.REPY_START_DIR) 
    334  
     264  # set up the circular log buffer... 
    335265  # Armon: Initialize the circular logger before starting the nanny 
    336   if logfile: 
     266  if options.logfile: 
    337267    # time to set up the circular logger 
    338     loggerfo = loggingrepy.circular_logger(logfile) 
     268    loggerfo = loggingrepy.circular_logger(options.logfile) 
    339269    # and redirect err and out there... 
    340270    sys.stdout = loggerfo 
     
    343273    # let's make it so that the output (via print) is always flushed 
    344274    sys.stdout = loggingrepy.flush_logger(sys.stdout) 
    345  
    346  
    347   # we need to give the left over arguments to repy so it can function 
    348   return extraargs 
     275     
     276  # Set Current Working Directory 
     277  if options.cwd: 
     278    os.chdir(options.cwd) 
     279 
     280  # Update repy current directory 
     281  repy_constants.REPY_CURRENT_DIR = os.path.abspath(os.getcwd()) 
     282 
     283  # Initialize the NM status interface 
     284  nmstatusinterface.init(options.stopfile, options.statusfile) 
     285   
     286  # Write out our initial status 
     287  statusstorage.write_status("Started") 
     288 
     289 
     290  # We also need to pass in whether or not we are going to be using the service 
     291  # log for repy.  We provide the repy directory so that the vessel information 
     292  # can be found regardless of where we are called from... 
     293  tracebackrepy.initialize(options.servicelog, repy_constants.REPY_START_DIR) 
     294 
    349295 
    350296 
     
    380326  ### PARSE OPTIONS.   These are command line in our case, but could be from 
    381327  ### anywhere if this is repurposed... 
    382  
    383   args = sys.argv[1:] 
    384  
    385   # do a huge amount of initialization. 
    386   # The repy location must be set first!!! 
    387   remainingargs = init_commandline_options(args) 
    388  
    389  
    390   # what remains is the resourcefn progname [args...] 
    391   resourcefn = remainingargs[0] 
    392   progname = remainingargs[1] 
    393   progargs = remainingargs[2:] 
    394  
     328  usage = "USAGE: repy.py [options] resource_file program_to_run.repy [program args]" 
     329  parser = optparse.OptionParser(usage=usage) 
     330  add_repy_options(parser) 
     331  options, args = parser.parse_args() 
     332   
     333  if len(args) < 2: 
     334    print "Repy requires a resource file and the program to run!" 
     335    parser.print_help() 
     336    sys.exit(1) 
     337   
     338  resourcefn = args[0] 
     339  progname = args[1] 
     340  progargs = args[2:] 
     341   
     342  # Do a huge amount of initialization. 
     343  parse_options(options) 
     344   
    395345  ### start resource restrictions, etc. for the nanny 
    396346  initialize_nanny(resourcefn) 
    397347 
    398  
    399   # grab the user code from the file 
     348  # Read the user code from the file 
    400349  try: 
    401350    filehandle = open(progname) 
     
    403352    filehandle.close() 
    404353  except: 
    405     print "Failed to read the specified program file: '"+progname+"'" 
    406     raise 
     354    print "FATAL ERROR: Unable to read the specified program file: '%s'" % (progname) 
     355    sys.exit(1) 
    407356 
    408357  # create the namespace... 
     
    415364 
    416365  # allow the (potentially large) code string to be garbage collected 
    417   usercode = None 
    418  
     366  del usercode 
    419367 
    420368  # get a new namespace 
     
    424372  # to contain an additional function. 
    425373 
    426  
    427374  # run the code to completion... 
    428375  execute_namespace_until_completion(newnamespace, newcontext) 
    429  
    430376 
    431377  # No more pending events for the user thread, we exit