From 0c228ba1165cda76d90c9c8cc2218a240e10c028 Mon Sep 17 00:00:00 2001 From: Tony Mack Date: Fri, 13 Jul 2012 22:35:29 -0400 Subject: [PATCH] remove myplc-config dependency --- config/sfa-config-tty | 501 +++++++++++++++++++++++++++++++++++++++++- sfa.spec | 1 - sfa/util/config.py | 202 +++++++++-------- 3 files changed, 610 insertions(+), 94 deletions(-) diff --git a/config/sfa-config-tty b/config/sfa-config-tty index 4008cd1c..26f48926 100755 --- a/config/sfa-config-tty +++ b/config/sfa-config-tty @@ -1,8 +1,15 @@ #!/bin/env python +import os import sys +import re +import time +import traceback +import types import readline -import plc_config +from StringIO import StringIO +from optparse import OptionParser +from sfa.util.config import Config def validator(validated_variables): pass @@ -34,7 +41,497 @@ configuration={ \ 'validator':validator, } + +# GLOBAL VARIABLES +# +release_id = "$Id$" +release_rev = "$Revision$" +release_url = "$URL$" + +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\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 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() + +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 : +#\tShow variable comments +.\tStops prompting, return to mainloop +/\tCleans any site-defined value, reverts to default +=\tShows default value +>\tSkips to next category +?\tThis help +""" + +#################### +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: + result=get_value (cwrite,category_id,variable_id) + except: + 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: + print "!!! No comment associated to %s_%s" % (cid,vid) + +#################### +def list_categories (config): + result=[] + for (category_id, (category, variables)) in config.variables().iteritems(): + result += [category_id] + 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 (category_id, (category, variables)) in config.variables().iteritems(): + if (cid == category_id): + for variable in variables.values(): + result += ["%s_%s" %(cid,variable['id'])] + 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, 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 (): + global service + os.system("set -x ; service %s reload" % service) + +#################### +def restart_service (): + global service + print ("==================== Stopping %s" % service) + os.system("service %s stop" % service) + print ("==================== Starting %s" % service) + os.system("service %s start" % service) + +#################### +def prompt_variable (cdef, cread, cwrite, category, variable, + show_comments, support_next=False): + + + category_id = category + variable_id = variable + + while True: + 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 = raw_input(prompt).strip() + except EOFError : + raise Exception ('BailOut') + except KeyboardInterrupt: + print "\n" + raise Exception ('BailOut') + + # no change + if (answer == "") or (answer == current_value): + return None + elif (answer == "."): + raise Exception ('BailOut') + elif (answer == "#"): + print_name_comments(cread,category_id,variable_id) + elif (answer == "?"): + print variable_usage.strip() + elif (answer == "="): + 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: + if cdef.validate_type(variable_type, answer): + cwrite.set(category_id, variable_id, answer) + else: + print "Not a valid value" + +def prompt_variables_all (cdef, cread, cwrite, show_comments): + try: + for (category_id, (category, variables)) in cread.variables().iteritems(): + print ("========== Category = %s" % category_id.upper()) + for variable in variables.values(): + try: + newvar = prompt_variable (cdef, cread, cwrite, category, variable, + show_comments, True) + except Exception, inst: + if (str(inst) == 'NextCategory'): break + else: raise + + except Exception, 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, inst: + if (str(inst) == 'BailOut'): return + else: raise + +#################### +def show_variable (cdef, cread, cwrite, + category, variable,show_value,show_comments): + assert category.has_key('id') + assert variable.has_key('id') + + 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().iteritems(): + print ("========== Category = %s" % category_id.upper()) + for variable in 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() + except EOFError: + answer ="" + except KeyboardInterrupt: + print "\nBye" + sys.exit() + + if (answer == "") or (answer in "?hH"): + print mainloop_usage + continue + 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: + # 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 + 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) + except Exception, inst: + if (str(inst) != 'BailOut'): + raise + 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, 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) + + +#################### +# 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,0755) + except OSError, e: + print "Cannot create dir %s due to %s - exiting" % (dirname,e) + sys.exit(1) + + 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 " + release_rev + release_url ) + 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") + + 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 + try: + cwrite=Config(config_filename) + except ConfigParser.MissingSectionHeaderError: + # remove legacy config + os.unlink(config_filename) + cwrite=Config(config_filename) + + try: + cread.load(site_config) + cwrite.load(site_config) + except: + cwrite = Config() + + mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config) + return 0 + if __name__ == '__main__': command=sys.argv[0] argv = sys.argv[1:] - plc_config.main(command,argv,configuration) + main(command,argv,configuration) diff --git a/sfa.spec b/sfa.spec index 229379bc..b11c0e4f 100644 --- a/sfa.spec +++ b/sfa.spec @@ -25,7 +25,6 @@ Summary: the SFA python libraries Group: Applications/System BuildRequires: make -Requires: myplc-config Requires: python >= 2.5 Requires: pyOpenSSL >= 0.7 Requires: m2crypto diff --git a/sfa/util/config.py b/sfa/util/config.py index eaf2aeac..e46b7712 100644 --- a/sfa/util/config.py +++ b/sfa/util/config.py @@ -1,96 +1,116 @@ -## -# SFA Configuration Info -# -# This module holds configuration parameters for SFA. There are two -# main pieces of information that are used: the database connection and -# the PLCAPI connection -## +#!/usr/bin/python +import sys +import os +import ConfigParser +import tempfile +from sfa.util.xml import XML -## -# SFA uses a MYSQL database to store records. This database may be -# co-located with the PLC database, or it may be a separate database. The -# following parameters define the connection to the database. -# -# Note that SFA does not access any of the PLC databases directly via -# a mysql connection; All PLC databases are accessed via PLCAPI. - -import os.path -import traceback +default_config = \ +""" +""" class Config: - """ - Parse the bash/Python/PHP version of the configuration file. Very - fast but no type conversions. - """ - - def __init__(self, config_file = "/etc/sfa/sfa_config.py"): - self.config_file = None - self.config_path = None - self.data_path = None - self.load(config_file) - - def load(self, config_file): - try: - execfile(config_file, self.__dict__) - self.config_file = config_file - # path to configuration data - self.config_path = os.path.dirname(config_file) - - ### xxx todo implement defaults in default_config.xml - # path to server data - if not hasattr(self, 'SFA_DATA_DIR'): - # default to /var/lib/sfa not specified in config - self.SFA_DATA_DIR="/var/lib/sfa" - self.data_path = self.SFA_DATA_DIR + + def __init__(self, config_file='/etc/sfa/sfa_config'): + self.config = ConfigParser.ConfigParser() + self.filename = config_file + if not os.path.isfile(self.filename): + self.create(self.filename) + self.load(self.filename) + + def create(self, filename): + if not os.path.exists(os.path.dirname(filename)): + os.makedirs(os.path.dirname(filename)) + configfile = open(filename, 'w') + configfile.write(default_config) + configfile.close() + + + def load(self, filename): + if filename: + if filename.endswith('.xml'): + try: + self.load_xml(filename) + except: + raise + self.config.read(filename) else: - self.data_path = self.SFA_DATA_DIR + self.config.read(filename) + self.set_attributes() - # path to config data - if not hasattr(self, 'SFA_CONFIG_DIR'): - # default to /etc/sfa not specified in config - self.SFA_CONFIG_DIR="/etc/sfa" - - if not hasattr(self, 'SFA_REGISTRY_LEVEL1_AUTH'): - self.SFA_REGISTRY_LEVEL1_AUTH=None - - # create the data directory if it doesnt exist - if not os.path.isdir(self.SFA_DATA_DIR): - try: - os.mkdir(self.SFA_DATA_DIR) - except: pass - - except IOError, e: - raise IOError, "Could not find or load the configuration file: %s" % config_file - - def get_trustedroots_dir(self): - return self.config_path + os.sep + 'trusted_roots' - - def get_openflow_aggrMgr_info(self): - aggr_mgr_ip = 'localhost' - if (hasattr(self,'OPENFLOW_AGGREGATE_MANAGER_IP')): - aggr_mgr_ip = self.OPENFLOW_AGGREGATE_MANAGER_IP - - aggr_mgr_port = 2603 - if (hasattr(self,'OPENFLOW_AGGREGATE_MANAGER_PORT')): - aggr_mgr_port = self.OPENFLOW_AGGREGATE_MANAGER_PORT - - return (aggr_mgr_ip,aggr_mgr_port) - - def get_interface_hrn(self): - if (hasattr(self,'SFA_INTERFACE_HRN')): - return self.SFA_INTERFACE_HRN - else: - return "plc" - - # TODO: find a better place to put this method - def get_max_aggrMgr_info(self): - am_apiclient_path = '/usr/local/MAXGENI_AM_APIClient' - if (hasattr(self,'MAXGENI_AM_APICLIENT_PATH')): - am_client_path = self.MAXGENI_AM_APICLIENT_PATH - - am_url = 'https://geni.dragon.maxgigapop.net:8443/axis2/services/AggregateGENI' - if (hasattr(self,'MAXGENI_AM_URL')): - am_url = self.MAXGENI_AM_URL - - return (am_apiclient_path,am_url) - + def load_xml(self, filename): + xml = XML(filename) + categories = xml.xpath('//configuration/variables/category') + for category in categories: + section_name = category.get('id') + if not self.config.has_section(section_name): + self.config.add_section(section_name) + options = category.xpath('./variablelist/variable') + for option in options: + option_name = option.get('id') + value = option.xpath('./value')[0].text + if not value: + value = "" + self.config.set(section_name, option_name, value) + + + def locate_varname(self, varname): + varname = varname.lower() + sections = self.config.sections() + section_name = "" + var_name = "" + for section in sections: + if varname.startswith(section.lower()) and len(section) > len(section_name): + section_name = section.lower() + var_name = varname.replace(section_name, "")[1:] + if not self.config.has_option(section_name, var_name): + raise ConfigParser.NoOptionError(varname, section_name) + return (section_name, var_name) + + def set_attributes(self): + sections = self.config.sections() + for section in sections: + for item in self.config.items(section): + name = "%s_%s" % (section, item[0]) + value = item[1] + setattr(self, name, value) + setattr(self, name.upper(), value) + + + def verify(self, config1, config2, validate_method): + return True + + def validate_type(self, var_type, value): + return True + + def dump(self, sections = []): + if not sections: + sections = self.config.sections() + print "" + for section in sections: + print "[%s]" % section + for item in self.config.items(section): + print "%s=%s" % (item[0], item[1]) + print "" + + def write(self, filename=None): + if not filename: + filename = self.filename + configfile = open(filename, 'w') + self.config.write(configfile) + + def save(self, filename=None): + self.write(filename) + + def __getattr__(self, attr): + return getattr(self.config, attr) + +if __name__ == '__main__': + filename = None + if len(sys.argv) > 1: + filename = sys.argv[1] + config = Config(filename) + else: + config = Config() + config.dump() + -- 2.43.0