X-Git-Url: http://git.onelab.eu/?p=sfa.git;a=blobdiff_plain;f=config%2Fsfa-config-tty;h=f7c234b5ef615c6a444656d177fb5249fde8bfd8;hp=da23887582ed64c0b421bb45f2321d106c7470b4;hb=HEAD;hpb=a2e21bef1545bbb68c86ec1a84a611bde7f17a45 diff --git a/config/sfa-config-tty b/config/sfa-config-tty index da238875..f7c234b5 100755 --- a/config/sfa-config-tty +++ b/config/sfa-config-tty @@ -1,302 +1,599 @@ -#!/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.trust.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 []\tShow Locally modified variables/values - s/S []\tShow all current variables/values - e/E []\tEdit variables (all, in category, single) + l/L [|]\tShow Locally modified variables/values + s/S [|]\tShow variables/values (all, in category, single) + e/E [|]\tEdit variables (all, in category, single) --- -""" + c\t\t\tList categories + v/V [|]\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[uUwrRqlLsSeEcvVhH\?])[ \t]*$" +re_mainloop_1arg = "^(?P[sSeEvV])[ \t]+(?P\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 sfa-import-plc.py 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)