a little nicer wrt pep8
[sfa.git] / config / sfa-config-tty
index 2031acd..f7c234b 100755 (executable)
-#!/usr/bin/python
+#!/usr/bin/env python3
 
-# Interactively prompts for variable values
-# expected arguments are
-# command -d [default-xml [custom-xml [ consolidated-xml ]]]
-#
-# -d is for the myplc-devel package
-
-# we use 3 instances of PLCConfiguration throughout:
-# cdef : models the defaults, from plc_default.xml
-# cread : merged from plc_default & configs/site.xml
-# cwrite : site.xml + pending changes
-
-import sys
 import os
+import sys
 import re
-import readline
 import traceback
-import distutils.file_util
+import readline
 from optparse import OptionParser
 
+from sfa.util.version import version_tag
 from sfa.util.config import Config
-from sfa.util.hierarchy import *
-from sfa.util.misc import *
-
-
-all_variables   = ["GENI_REGISTRY_ROOT_AUTH",
-                   "GENI_REGISTRY_LEVEL1_AUTH",
-                   "GENI_REGISTRY_ENABLED",
-                   "GENI_REGISTRY_HOST", 
-                   "GENI_REGISTRY_PORT",
-                   "GENI_AGGREGATE_ENABLED",
-                   "GENI_AGGREGATE_HOST",
-                   "GENI_AGGREGATE_PORT",
-                   "GENI_SM_ENABLED",
-                   "GENI_SM_HOST",
-                   "GENI_SM_PORT",
-                   "GENI_PLC_USER",
-                   "GENI_PLC_PASSWORD",    
-                   "GENI_PLC_URL",
-                   ]
-usual_variables = ["GENI_REGISTRY_ROOT_AUTH",
-                   "GENI_REGISTRY_LEVEL1_AUTH",
-                   "GENI_PLC_USER",
-                   "GENI_PLC_PASSWORD",    
-                   ]
-
-
-mainloop_usage= """Available commands:
+
+
+def validator(validated_variables):
+    pass
+#    maint_user = validated_variables["PLC_API_MAINTENANCE_USER"]
+#    root_user = validated_variables["PLC_ROOT_USER"]
+#    if maint_user == root_user:
+#        errStr="PLC_API_MAINTENANCE_USER=%s cannot be the same as PLC_ROOT_USER=%s"%(maint_user, root_user)
+#        raise plc_config.ConfigurationException(errStr)
+
+usual_variables = [
+    "SFA_GENERIC_FLAVOUR",
+    "SFA_INTERFACE_HRN",
+    "SFA_REGISTRY_ROOT_AUTH",
+    "SFA_REGISTRY_HOST",
+    "SFA_AGGREGATE_HOST",
+    "SFA_DB_HOST",
+]
+
+flavour_xml_section_hash = {
+    'pl': 'sfa_plc',
+    'dummy': 'sfa_dummy',
+}
+configuration = {
+    'name': 'sfa',
+    'service': "sfa",
+    'usual_variables': usual_variables,
+    'config_dir': "/etc/sfa",
+    'validate_variables': {},
+    'validator': validator,
+}
+
+
+# GLOBAL VARIABLES
+#
+g_configuration = None
+usual_variables = None
+config_dir = None
+service = None
+
+
+def noop_validator(validated_variables):
+    pass
+
+# historically we could also configure the devel pkg....
+
+
+def init_configuration():
+    global g_configuration
+    global usual_variables, config_dir, service
+
+    usual_variables = g_configuration["usual_variables"]
+    config_dir = g_configuration["config_dir"]
+    service = g_configuration["service"]
+
+    global def_default_config, def_site_config, def_consolidated_config
+    def_default_config = "%s/default_config.xml" % config_dir
+    def_site_config = "%s/configs/site_config" % config_dir
+    def_consolidated_config = "%s/%s_config" % (config_dir, service)
+
+    global mainloop_usage
+    mainloop_usage = """Available commands:
+ Uppercase versions give variables comments, when available
  u/U\t\t\tEdit usual variables
- w/W\t\t\tWrite / Write & reload
+ w\t\t\tWrite
+ r\t\t\tRestart %(service)s service
+ R\t\t\tReload %(service)s service (rebuild config files for sh, python....)
  q\t\t\tQuit (without saving)
  h/?\t\t\tThis help
 ---
- l/L [<var>]\tShow Locally modified variables/values
- s/S [<var>]\tShow all current variables/values 
- e/E [<var>]\tEdit variables (all, in category, single)
+ l/L [<cat>|<var>]\tShow Locally modified variables/values
+ s/S [<cat>|<var>]\tShow variables/values (all, in category, single)
+ e/E [<cat>|<var>]\tEdit variables (all, in category, single)
 ---
-""" 
+ c\t\t\tList categories
+ v/V [<cat>|<var>]\tList Variables (all, in category, single)
+---
+Typical usage involves: u, [l,] w, r, q
+""" % globals()
+
 
-command_usage="%prog [options]"
-command_usage += """
-  Unless you specify the -d option, meaning you want to configure
-  using defaults without interactive prompts""" 
+def usage():
+    command_usage = "%prog [options] [default-xml [site-xml [consolidated-xml]]]"
+    init_configuration()
+    command_usage += """
+\t default-xml defaults to %s
+\t site-xml defaults to %s
+\t consolidated-xml defaults to %s""" % (def_default_config, def_site_config, def_consolidated_config)
+    return command_usage
 
-variable_usage= """Edit Commands :
+####################
+variable_usage = """Edit Commands :
+#\tShow variable comments
 .\tStops prompting, return to mainloop
+/\tCleans any site-defined value, reverts to default
 =\tShows default value
-?\tThis help    
+>\tSkips to next category
+?\tThis help
 """
 
-def save_config(changes, config_file):
-    # always validate before saving
-    changes = validate(changes) 
-    
-    cfile = open(config_file, 'r')
-    lines = cfile.readlines()
-    cfile.close()
-    newlines = []
-    for line in lines:
-        added = False
-        for variable in changes:
-            if line.startswith(variable+'='):
-                try:
-                    value = int(changes[variable])
-                    newline = '%s=%s\n' % (variable, value)
-                    newlines.append(newline)
-                except:
-                    value = changes[variable]
-                    newline = '%s="%s"\n' % (variable, value)
-                    newlines.append(newline)
-                added = True
-                break
-        if not added:
-            newlines.append(line) 
-    
-    cfile = open(config_file, 'w')
-    cfile.writelines(newlines)
-    cfile.close()
-    print 'updated config file',config_file
-
-def validate(changes):
-
-    if not changes:
-        return {}
-
-    defaults = get_defaults()
-    
-    # GENI_INTERFACE_HRN is GENI_REGISTRY_LEVEL1_AUTH, if thats blank it
-    # then defaults to GENI_REGISTRY_ROOT_AUTH 
-    # GENI_REGISTRY_LEVEL1_AUTH, so if either of these are present we must 
-    # update GENI_INTERFACE_HRN
-    if 'GENI_REGISTRY_ROOT_AUTH' in changes:
-        root_auth = changes['GENI_REGISTRY_ROOT_AUTH']
-    else:
-        root_auth = defaults['GENI_REGISTRY_ROOT_AUTH']
-             
-    if 'GENI_REGISTRY_LEVEL1_AUTH' in changes:
-        level1_auth = changes['GENI_REGISTRY_LEVEL1_AUTH']
-    else:
-        level1_auth = defaults['GENI_REGISTRY_LEVEL1_AUTH']
-                
-    if level1_auth:
-        interface_hrn = level1_auth
-    else:
-        interface_hrn = root_auth
-    changes['GENI_INTERFACE_HRN'] = interface_hrn
-    return changes                            
-
-def get_defaults():
-    sfa_config = Config()
-    plc_vars = {'PLC_API_MAINTENANCE_PASSWORD': 'GENI_PLC_PASSWORD',
-                'PLC_API_MAINTENANCE_USER': 'GENI_PLC_USER'
-               }
+####################
+
+
+def get_value(config,  category_id, variable_id):
+    value = config.get(category_id, variable_id)
+    return value
+
+
+def get_type(config, category_id, variable_id):
+    value = config.get(category_id, variable_id)
+    # return variable['type']
+    return str
+
+
+def get_current_value(cread, cwrite, category_id, variable_id):
+    # the value stored in cwrite, if present, is the one we want
     try:
-        from sfa.util.config import plcConfig
-        plc_config = plcConfig
+        result = get_value(cwrite, category_id, variable_id)
     except:
-        plc_config = None
-    
-    defaults = {}
-    for var in dir(sfa_config):
-        if var.startswith('GENI'):
-            value = eval("sfa_config.%s" % var)
-            defaults[var] = value
-
-    # some defaults come from plc_config
-    for var in dir(plc_config):
-        if var in plc_vars:
-            value = eval("plc_config.%s" % var)
-            defaults[plc_vars[var]] = value
-
-    return defaults       
-
-def prompt_variable(variable, default_config):
-    if variable in default_config:
-        default_value = default_config[variable]
+        result = get_value(cread, category_id, variable_id)
+    return result
+
+# refrain from using plc_config's _sanitize
+
+
+def get_varname(config,  category_id, variable_id):
+    varname = category_id + "_" + variable_id
+    config.locate_varname(varname)
+    return varname
+
+# could not avoid using _sanitize here..
+
+
+def get_name_comments(config, cid, vid):
+    try:
+        (category, variable) = config.get(cid, vid)
+        (id, name, value, comments) = config._sanitize_variable(cid, variable)
+        return (name, comments)
+    except:
+        return (None, [])
+
+
+def print_name_comments(config, cid, vid):
+    name, comments = get_name_comments(config, cid, vid)
+    if name:
+        print("### %s" % name)
+    if comments:
+        for line in comments:
+            print("# %s" % line)
     else:
-        default_value = ""  
-   
+        print("!!! No comment associated to %s_%s" % (cid, vid))
+
+####################
+
+
+def list_categories(config):
+    result = []
+    for section in config.sections():
+        result += [section]
+    return result
+
+
+def print_categories(config):
+    print("Known categories")
+    for cid in list_categories(config):
+        print("%s" % (cid.upper()))
+
+####################
+
+
+def list_category(config, cid):
+    result = []
+    for section in config.sections():
+        if section == cid.lower():
+            for (name, value) in config.items(section):
+                result += ["%s_%s" % (cid, name)]
+    return result
+
+
+def print_category(config, cid, show_comments=True):
+    cid = cid.lower()
+    CID = cid.upper()
+    vids = list_category(config, cid)
+    if (len(vids) == 0):
+        print("%s : no such category" % CID)
+    else:
+        print("Category %s contains" % (CID))
+        for vid in vids:
+            print(vid.upper())
+
+####################
+
+
+def consolidate(default_config, site_config, consolidated_config):
+    global service
+    try:
+        conso = Config(default_config)
+        conso.load(site_config)
+        conso.save(consolidated_config)
+    except Exception as inst:
+        print("Could not consolidate, %s" % (str(inst)))
+        return
+    print(("Merged\n\t%s\nand\t%s\ninto\t%s" % (default_config, site_config,
+                                               consolidated_config)))
+
+
+def reload_service():
+    reload = "sfa-setup.sh reload"
+    print(("Running: {}".format(reload)))
+    os.system(reload)
+
+####################
+
+
+def restart_service():
+    services = ('sfa-db', 'sfa-aggregate', 'sfa-registry')
+    for service in services:
+        restart = ("systemctl -q is-active {s} && "
+                   "{{ echo restarting {s} ; systemctl restart {s}; }}"
+                   .format(s=service))
+        os.system(restart)
+
+####################
+
+
+def prompt_variable(cdef, cread, cwrite, category, variable,
+                    show_comments, support_next=False):
+
+    category_id = category
+    variable_id = variable
+
     while True:
-        prompt = "%(variable)s : [%(default_value)s] " % locals()
-        try: 
-            answer = raw_input(prompt).strip()
+        default_value = get_value(cdef, category_id, variable_id)
+        variable_type = get_type(cdef, category_id, variable_id)
+        current_value = get_current_value(
+            cread, cwrite, category_id, variable_id)
+        varname = get_varname(cread, category_id, variable_id)
+
+        if show_comments:
+            print_name_comments(cdef, category_id, variable_id)
+        prompt = "== %s : [%s] " % (varname, current_value)
+        try:
+            answer = input(prompt).strip()
         except EOFError:
-            raise Exception ('BailOut')
+            raise Exception('BailOut')
         except KeyboardInterrupt:
-            print "\n"
-            raise Exception ('BailOut')
+            print("\n")
+            raise Exception('BailOut')
 
-        if (answer == "") or (answer == default_value):
-            return default_value
-        elif answer in ['""', "''"]:
-            return ""
+        # no change
+        if (answer == "") or (answer == current_value):
+            return None
         elif (answer == "."):
-            raise Exception ('BailOut')
+            raise Exception('BailOut')
+        elif (answer == "#"):
+            print_name_comments(cread, category_id, variable_id)
         elif (answer == "?"):
-            print variable_usage.strip()
+            print(variable_usage.strip())
         elif (answer == "="):
-            print ("%s defaults to %s" %(variable,default_value))
+            print(("%s defaults to %s" % (varname, default_value)))
+        # revert to default : remove from cwrite (i.e. site-config)
+        elif (answer == "/"):
+            cwrite.delete(category_id, variable_id)
+            print(("%s reverted to %s" % (varname, default_value)))
+            return
+        elif (answer == ">"):
+            if support_next:
+                raise Exception('NextCategory')
+            else:
+                print("No support for next category")
         else:
-            return answer
+            if cdef.validate_type(variable_type, answer):
+                cwrite.set(category_id, variable_id, answer)
+                return
+            else:
+                print("Not a valid value")
 
-def show_variable(variable, value_dict):
-    print "%s=%s" % (variable, value_dict[variable])                    
-    
-def mainloop (default_config, config_file):
-    changes = {}
+
+def prompt_variables_all(cdef, cread, cwrite, show_comments):
+    try:
+        for (category_id, (category, variables)) in cread.variables().items():
+            print(("========== Category = %s" % category_id.upper()))
+            for variable in list(variables.values()):
+                try:
+                    newvar = prompt_variable(cdef, cread, cwrite, category, variable,
+                                             show_comments, True)
+                except Exception as inst:
+                    if (str(inst) == 'NextCategory'):
+                        break
+                    else:
+                        raise
+
+    except Exception as inst:
+        if (str(inst) == 'BailOut'):
+            return
+        else:
+            raise
+
+
+def prompt_variables_category(cdef, cread, cwrite, cid, show_comments):
+    cid = cid.lower()
+    CID = cid.upper()
+    try:
+        print(("========== Category = %s" % CID))
+        for vid in list_category(cdef, cid):
+            (category, variable) = cdef.locate_varname(vid.upper())
+            newvar = prompt_variable(cdef, cread, cwrite, category, variable,
+                                     show_comments, False)
+    except Exception as inst:
+        if (str(inst) == 'BailOut'):
+            return
+        else:
+            raise
+
+####################
+
+
+def show_variable(cdef, cread, cwrite,
+                  category, variable, show_value, show_comments):
+    assert 'id' in category
+    assert 'id' in variable
+
+    category_id = category['id']
+    variable_id = variable['id']
+
+    default_value = get_value(cdef, category_id, variable_id)
+    current_value = get_current_value(cread, cwrite, category_id, variable_id)
+    varname = get_varname(cread, category_id, variable_id)
+    if show_comments:
+        print_name_comments(cdef, category_id, variable_id)
+    if show_value:
+        print("%s = %s" % (varname, current_value))
+    else:
+        print("%s" % (varname))
+
+
+def show_variables_all(cdef, cread, cwrite, show_value, show_comments):
+    for (category_id, (category, variables)) in cread.variables().items():
+        print(("========== Category = %s" % category_id.upper()))
+        for variable in list(variables.values()):
+            show_variable(cdef, cread, cwrite,
+                          category, variable, show_value, show_comments)
+
+
+def show_variables_category(cdef, cread, cwrite, cid, show_value, show_comments):
+    cid = cid.lower()
+    CID = cid.upper()
+    print(("========== Category = %s" % CID))
+    for vid in list_category(cdef, cid):
+        (category, variable) = cdef.locate_varname(vid.upper())
+        show_variable(cdef, cread, cwrite, category, variable,
+                      show_value, show_comments)
+
+####################
+re_mainloop_0arg = "^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
+re_mainloop_1arg = "^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
+matcher_mainloop_0arg = re.compile(re_mainloop_0arg)
+matcher_mainloop_1arg = re.compile(re_mainloop_1arg)
+
+
+def mainloop(cdef, cread, cwrite, default_config, site_config, consolidated_config):
+    global service
     while True:
         try:
-            answer = raw_input("Enter command (u for usual changes, w to save, ? for help) ").strip()
+            answer = input(
+                "Enter command (u for usual changes, w to save, ? for help) ").strip()
         except EOFError:
-            answer =""
+            answer = ""
         except KeyboardInterrupt:
-            print "\nBye"
+            print("\nBye")
             sys.exit()
 
         if (answer == "") or (answer in "?hH"):
-            print mainloop_usage
+            print(mainloop_usage)
             continue
-        if answer in ['?']:
-            print "help"  
-
-        if (answer in ["q","Q"]):
-            break
-        elif (answer == "w"):
-            save_config(changes, config_file)
-        elif (answer == "u"):
-            try: 
-                for varname in usual_variables:
-                    changes[varname] = prompt_variable(varname, default_config)
-            except Exception, inst:
-                if (str(inst) != 'BailOut'):
-                    raise
-        elif (answer in ["e","E"]):
+        groups_parse = matcher_mainloop_0arg.match(answer)
+        command = None
+        if (groups_parse):
+            command = groups_parse.group('command')
+            arg = None
+        else:
+            groups_parse = matcher_mainloop_1arg.match(answer)
+            if (groups_parse):
+                command = groups_parse.group('command')
+                arg = groups_parse.group('arg')
+        if not command:
+            print(("Unknown command >%s< -- use h for help" % answer))
+            continue
+
+        show_comments = command.isupper()
+
+        mode = 'ALL'
+        if arg:
+            mode = None
+            arg = arg.lower()
+            variables = list_category(cdef, arg)
+            if len(variables):
+                # category_id as the category name
+                # variables as the list of variable names
+                mode = 'CATEGORY'
+                category_id = arg
+            arg = arg.upper()
+            (category, variable) = cdef.locate_varname(arg)
+            if variable:
+                # category/variable as output by locate_varname
+                mode = 'VARIABLE'
+            if not mode:
+                print("%s: no such category or variable" % arg)
+                continue
+
+        if command in "qQ":
+            # todo check confirmation
+            return
+        elif command == "w":
             try:
-                prompt_variable (cdef,cread,cwrite,category,variable,
-                                 show_comments,False)
-            except Exception, inst:
+                # Confirm that various constraints are met before saving file.
+                validate_variables = g_configuration.get(
+                    'validate_variables', {})
+                validated_variables = cwrite.verify(
+                    cdef, cread, validate_variables)
+                validator = g_configuration.get('validator', noop_validator)
+                validator(validated_variables)
+                cwrite.save(site_config)
+            except:
+                print("Save failed due to a configuration exception:")
+                print(traceback.print_exc())
+                print(("Could not save -- fix write access on %s" % site_config))
+                break
+            print(("Wrote %s" % site_config))
+            consolidate(default_config, site_config, consolidated_config)
+            print(("You might want to type 'r' (restart %s), 'R' (reload %s) or 'q' (quit)" %
+                  (service, service)))
+        elif command in "uU":
+            global usual_variables
+            global flavour_xml_section_hash
+            try:
+                for varname in usual_variables:
+                    (category, variable) = cdef.locate_varname(varname)
+                    if not (category is None and variable is None):
+                        prompt_variable(cdef, cread, cwrite,
+                                        category, variable, False)
+
+                # set the driver variable according to the already set flavour
+                generic_flavour = cwrite.items('sfa')[0][1]
+                for section in cdef.sections():
+                    if generic_flavour in flavour_xml_section_hash and flavour_xml_section_hash[generic_flavour] == section:
+                        for item in cdef.items(section):
+                            category = section
+                            variable = item[0]
+                            prompt_variable(cdef, cread, cwrite,
+                                            category, variable, False)
+                        break
+
+            except Exception as inst:
                 if (str(inst) != 'BailOut'):
                     raise
-        elif (answer in "sS"):
-            for varname in usual_variables:
-                show_variable (varname, default_config)
-        elif (answer in "lL"):
-            if not changes:
-                print "No changes to display"
-            else:
-                for varname in changes:
-                    show_variable(varname, changes)
+        elif command == "r":
+            restart_service()
+        elif command == "R":
+            reload_service()
+        elif command == "c":
+            print_categories(cread)
+        elif command in "eE":
+            if mode == 'ALL':
+                prompt_variables_all(cdef, cread, cwrite, show_comments)
+            elif mode == 'CATEGORY':
+                prompt_variables_category(
+                    cdef, cread, cwrite, category_id, show_comments)
+            elif mode == 'VARIABLE':
+                try:
+                    prompt_variable(cdef, cread, cwrite, category, variable,
+                                    show_comments, False)
+                except Exception as inst:
+                    if str(inst) != 'BailOut':
+                        raise
+        elif command in "vVsSlL":
+            show_value = (command in "sSlL")
+            (c1, c2, c3) = (cdef, cread, cwrite)
+            if command in "lL":
+                (c1, c2, c3) = (cwrite, cwrite, cwrite)
+            if mode == 'ALL':
+                show_variables_all(c1, c2, c3, show_value, show_comments)
+            elif mode == 'CATEGORY':
+                show_variables_category(
+                    c1, c2, c3, category_id, show_value, show_comments)
+            elif mode == 'VARIABLE':
+                show_variable(c1, c2, c3, category, variable,
+                              show_value, show_comments)
         else:
-            print ("Unknown command >%s< -- use h for help" % answer)
+            print(("Unknown command >%s< -- use h for help" % answer))
 
-    result = {}
-    result.update(default_config)
-    result.update(changes)
-    return result
-    
-def setup_server_key(config_dict):
-    hrn = config_dict.get('GENI_INTERFACE_HRN')
-    if not hrn: return
-   
-    # Get the path to the authorities directory hierarchy
-    hierarchy = Hierarchy()
-    path = hierarchy.basedir
-    auth_path = hrn.replace(".", os.sep)
-    # define some useful variables   
-    key = 'server.key'
-    cert = 'server.cert'
-    hrn_leaf = get_leaf(hrn)
-    if not hrn_leaf:
-        hrn_leaf = hrn
-    new_server_key = os.sep.join([path, auth_path, hrn_leaf]) + ".pkey"
-    old_server_key = os.sep.join([path, key])
-    old_server_cert = os.sep.join([path, cert])
-
-    # remove old key/cert
-    for fd in [old_server_key, old_server_cert]:
-        if os.path.isfile(fd):
-            os.remove(fd)
-
-    # create new server.key
-    try:
-        distutils.file_util.copy_file(src=new_server_key, dst=old_server_key, verbose=1)
-        print "\t\t%(old_server_key)s\ncopied from\t%(new_server_key)s" % locals()
-    # this is expected when running this tool for the first time (before sfa-import-plc.py)
-    except:
-        print "Could not create %(old_server_key)s - ignore if you haven't run gimport yet"%locals()
-    
-    
 
 ####################
-def main ():
+# creates directory for file if not yet existing
+def check_dir(config_file):
+    dirname = os.path.dirname(config_file)
+    if (not os.path.exists(dirname)):
+        try:
+            os.makedirs(dirname, 0o755)
+        except OSError as e:
+            print("Cannot create dir %s due to %s - exiting" % (dirname, e))
+            sys.exit(1)
 
-    command=sys.argv[0]
-    argv = sys.argv[1:]
-    save = True
-    parser = OptionParser(usage=command_usage, version="%prog 1.0")
-    parser.set_defaults(config_dir="/etc/sfa",
-                        usual_variables=[])
-    parser.add_option("","--configdir",dest="config_dir",action="append", help="specify configuration directory")
-    parser.add_option("","--usual_variable",dest="usual_variables",action="append", help="add a usual variable")
-    parser.add_option("-d", "--default", action="count", help="dont prompt for values, just use defaults")
-    (config,args) = parser.parse_args()
-    if len(args)>3:
+        if (not os.path.exists(dirname)):
+            print("Cannot create dir %s - exiting" % dirname)
+            sys.exit(1)
+        else:
+            print("Created directory %s" % dirname)
+
+####################
+
+
+def optParserSetup(configuration):
+    parser = OptionParser(usage=usage(), version="%prog " + version_tag)
+    parser.set_defaults(config_dir=configuration['config_dir'],
+                        service=configuration['service'],
+                        usual_variables=configuration['usual_variables'])
+    parser.add_option("", "--configdir", dest="config_dir",
+                      help="specify configuration directory")
+    parser.add_option("", "--service", dest="service",
+                      help="specify /etc/init.d style service name")
+    parser.add_option("", "--usual_variable", dest="usual_variables",
+                      action="append", help="add a usual variable")
+    return parser
+
+
+def main(command, argv, configuration):
+    global g_configuration
+    g_configuration = configuration
+
+    parser = optParserSetup(configuration)
+    (config, args) = parser.parse_args()
+    if len(args) > 3:
         parser.error("too many arguments")
 
-    config_dir = parser.values.config_dir
-    config_file = os.sep.join([config_dir, 'sfa_config'])
-    defaults = get_defaults()
-    # if -d is specified dont prompt, just configure with defaults
-    if '-d' in argv:
-        save_config(defaults, config_file)
-        results = defaults
-    else:        
-        results = mainloop (defaults, config_file)
-    setup_server_key(results)
+    configuration['service'] = config.service
+    configuration['usual_variables'] = config.usual_variables
+    configuration['config_dir'] = config.config_dir
+    # add in new usual_variables defined on the command line
+    for usual_variable in config.usual_variables:
+        if usual_variable not in configuration['usual_variables']:
+            configuration['usual_variables'].append(usual_variable)
+
+    # intialize configuration
+    init_configuration()
+
+    default_config, site_config, consolidated_config = \
+        def_default_config, def_site_config, def_consolidated_config
+    if len(args) >= 1:
+        default_config = args[0]
+    if len(args) >= 2:
+        site_config = args[1]
+    if len(args) == 3:
+        consolidated_config = args[2]
+
+    for c in (default_config, site_config, consolidated_config):
+        check_dir(c)
+
+    try:
+        # the default settings only - read only
+        cdef = Config(default_config)
+
+        # in effect : default settings + local settings - read only
+        cread = Config(default_config)
+    except:
+        print(traceback.print_exc())
+        print(("default config files %s not found, is myplc installed ?" %
+              default_config))
+        return 1
+
+    # local settings only, will be modified & saved
+    config_filename = "%s/sfa_config" % config.config_dir
+    cwrite = Config(config_filename)
+    try:
+        cread.load(site_config)
+        cwrite.load(default_config)
+        cwrite.load(site_config)
+    except:
+        cwrite = Config()
+
+    mainloop(cdef, cread, cwrite, default_config,
+             site_config, consolidated_config)
     return 0
 
 if __name__ == '__main__':
-    main()
+    command = sys.argv[0]
+    argv = sys.argv[1:]
+    main(command, argv, configuration)