10 from StringIO import StringIO
11 from optparse import OptionParser
13 from sfa.util.version import version_tag
14 from sfa.util.config import Config
17 def validator(validated_variables):
19 # maint_user = validated_variables["PLC_API_MAINTENANCE_USER"]
20 # root_user = validated_variables["PLC_ROOT_USER"]
21 # if maint_user == root_user:
22 # errStr="PLC_API_MAINTENANCE_USER=%s cannot be the same as PLC_ROOT_USER=%s"%(maint_user, root_user)
23 # raise plc_config.ConfigurationException(errStr)
26 "SFA_GENERIC_FLAVOUR",
28 "SFA_REGISTRY_ROOT_AUTH",
35 flavour_xml_section_hash = {
37 'openstack': 'sfa_nova',
45 'usual_variables': usual_variables,
46 'config_dir': "/etc/sfa",
47 'validate_variables': {},
48 'validator': validator,
54 g_configuration = None
55 usual_variables = None
60 def noop_validator(validated_variables):
63 # historically we could also configure the devel pkg....
66 def init_configuration():
67 global g_configuration
68 global usual_variables, config_dir, service
70 usual_variables = g_configuration["usual_variables"]
71 config_dir = g_configuration["config_dir"]
72 service = g_configuration["service"]
74 global def_default_config, def_site_config, def_consolidated_config
75 def_default_config = "%s/default_config.xml" % config_dir
76 def_site_config = "%s/configs/site_config" % config_dir
77 def_consolidated_config = "%s/%s_config" % (config_dir, service)
80 mainloop_usage = """Available commands:
81 Uppercase versions give variables comments, when available
82 u/U\t\t\tEdit usual variables
84 r\t\t\tRestart %(service)s service
85 R\t\t\tReload %(service)s service (rebuild config files for sh, python....)
86 q\t\t\tQuit (without saving)
89 l/L [<cat>|<var>]\tShow Locally modified variables/values
90 s/S [<cat>|<var>]\tShow variables/values (all, in category, single)
91 e/E [<cat>|<var>]\tEdit variables (all, in category, single)
93 c\t\t\tList categories
94 v/V [<cat>|<var>]\tList Variables (all, in category, single)
96 Typical usage involves: u, [l,] w, r, q
101 command_usage = "%prog [options] [default-xml [site-xml [consolidated-xml]]]"
104 \t default-xml defaults to %s
105 \t site-xml defaults to %s
106 \t consolidated-xml defaults to %s""" % (def_default_config, def_site_config, def_consolidated_config)
110 variable_usage = """Edit Commands :
111 #\tShow variable comments
112 .\tStops prompting, return to mainloop
113 /\tCleans any site-defined value, reverts to default
114 =\tShows default value
115 >\tSkips to next category
122 def get_value(config, category_id, variable_id):
123 value = config.get(category_id, variable_id)
127 def get_type(config, category_id, variable_id):
128 value = config.get(category_id, variable_id)
129 # return variable['type']
133 def get_current_value(cread, cwrite, category_id, variable_id):
134 # the value stored in cwrite, if present, is the one we want
136 result = get_value(cwrite, category_id, variable_id)
138 result = get_value(cread, category_id, variable_id)
141 # refrain from using plc_config's _sanitize
144 def get_varname(config, category_id, variable_id):
145 varname = category_id + "_" + variable_id
146 config.locate_varname(varname)
149 # could not avoid using _sanitize here..
152 def get_name_comments(config, cid, vid):
154 (category, variable) = config.get(cid, vid)
155 (id, name, value, comments) = config._sanitize_variable(cid, variable)
156 return (name, comments)
161 def print_name_comments(config, cid, vid):
162 name, comments = get_name_comments(config, cid, vid)
164 print "### %s" % name
166 for line in comments:
169 print "!!! No comment associated to %s_%s" % (cid, vid)
174 def list_categories(config):
176 for section in config.sections():
181 def print_categories(config):
182 print "Known categories"
183 for cid in list_categories(config):
184 print "%s" % (cid.upper())
189 def list_category(config, cid):
191 for section in config.sections():
192 if section == cid.lower():
193 for (name, value) in config.items(section):
194 result += ["%s_%s" % (cid, name)]
198 def print_category(config, cid, show_comments=True):
201 vids = list_category(config, cid)
203 print "%s : no such category" % CID
205 print "Category %s contains" % (CID)
212 def consolidate(default_config, site_config, consolidated_config):
215 conso = Config(default_config)
216 conso.load(site_config)
217 conso.save(consolidated_config)
218 except Exception, inst:
219 print "Could not consolidate, %s" % (str(inst))
221 print("Merged\n\t%s\nand\t%s\ninto\t%s" % (default_config, site_config,
222 consolidated_config))
225 def reload_service():
227 os.system("set -x ; service %s reload" % service)
232 def restart_service():
234 print("==================== Stopping %s" % service)
235 os.system("service %s stop" % service)
236 print("==================== Starting %s" % service)
237 os.system("service %s start" % service)
242 def prompt_variable(cdef, cread, cwrite, category, variable,
243 show_comments, support_next=False):
245 category_id = category
246 variable_id = variable
249 default_value = get_value(cdef, category_id, variable_id)
250 variable_type = get_type(cdef, category_id, variable_id)
251 current_value = get_current_value(
252 cread, cwrite, category_id, variable_id)
253 varname = get_varname(cread, category_id, variable_id)
256 print_name_comments(cdef, category_id, variable_id)
257 prompt = "== %s : [%s] " % (varname, current_value)
259 answer = raw_input(prompt).strip()
261 raise Exception('BailOut')
262 except KeyboardInterrupt:
264 raise Exception('BailOut')
267 if (answer == "") or (answer == current_value):
269 elif (answer == "."):
270 raise Exception('BailOut')
271 elif (answer == "#"):
272 print_name_comments(cread, category_id, variable_id)
273 elif (answer == "?"):
274 print variable_usage.strip()
275 elif (answer == "="):
276 print("%s defaults to %s" % (varname, default_value))
277 # revert to default : remove from cwrite (i.e. site-config)
278 elif (answer == "/"):
279 cwrite.delete(category_id, variable_id)
280 print("%s reverted to %s" % (varname, default_value))
282 elif (answer == ">"):
284 raise Exception('NextCategory')
286 print "No support for next category"
288 if cdef.validate_type(variable_type, answer):
289 cwrite.set(category_id, variable_id, answer)
292 print "Not a valid value"
295 def prompt_variables_all(cdef, cread, cwrite, show_comments):
297 for (category_id, (category, variables)) in cread.variables().iteritems():
298 print("========== Category = %s" % category_id.upper())
299 for variable in variables.values():
301 newvar = prompt_variable(cdef, cread, cwrite, category, variable,
303 except Exception, inst:
304 if (str(inst) == 'NextCategory'):
309 except Exception, inst:
310 if (str(inst) == 'BailOut'):
316 def prompt_variables_category(cdef, cread, cwrite, cid, show_comments):
320 print("========== Category = %s" % CID)
321 for vid in list_category(cdef, cid):
322 (category, variable) = cdef.locate_varname(vid.upper())
323 newvar = prompt_variable(cdef, cread, cwrite, category, variable,
324 show_comments, False)
325 except Exception, inst:
326 if (str(inst) == 'BailOut'):
334 def show_variable(cdef, cread, cwrite,
335 category, variable, show_value, show_comments):
336 assert category.has_key('id')
337 assert variable.has_key('id')
339 category_id = category['id']
340 variable_id = variable['id']
342 default_value = get_value(cdef, category_id, variable_id)
343 current_value = get_current_value(cread, cwrite, category_id, variable_id)
344 varname = get_varname(cread, category_id, variable_id)
346 print_name_comments(cdef, category_id, variable_id)
348 print "%s = %s" % (varname, current_value)
350 print "%s" % (varname)
353 def show_variables_all(cdef, cread, cwrite, show_value, show_comments):
354 for (category_id, (category, variables)) in cread.variables().iteritems():
355 print("========== Category = %s" % category_id.upper())
356 for variable in variables.values():
357 show_variable(cdef, cread, cwrite,
358 category, variable, show_value, show_comments)
361 def show_variables_category(cdef, cread, cwrite, cid, show_value, show_comments):
364 print("========== Category = %s" % CID)
365 for vid in list_category(cdef, cid):
366 (category, variable) = cdef.locate_varname(vid.upper())
367 show_variable(cdef, cread, cwrite, category, variable,
368 show_value, show_comments)
371 re_mainloop_0arg = "^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
372 re_mainloop_1arg = "^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
373 matcher_mainloop_0arg = re.compile(re_mainloop_0arg)
374 matcher_mainloop_1arg = re.compile(re_mainloop_1arg)
377 def mainloop(cdef, cread, cwrite, default_config, site_config, consolidated_config):
382 "Enter command (u for usual changes, w to save, ? for help) ").strip()
385 except KeyboardInterrupt:
389 if (answer == "") or (answer in "?hH"):
392 groups_parse = matcher_mainloop_0arg.match(answer)
395 command = groups_parse.group('command')
398 groups_parse = matcher_mainloop_1arg.match(answer)
400 command = groups_parse.group('command')
401 arg = groups_parse.group('arg')
403 print("Unknown command >%s< -- use h for help" % answer)
406 show_comments = command.isupper()
412 variables = list_category(cdef, arg)
414 # category_id as the category name
415 # variables as the list of variable names
419 (category, variable) = cdef.locate_varname(arg)
421 # category/variable as output by locate_varname
424 print "%s: no such category or variable" % arg
428 # todo check confirmation
432 # Confirm that various constraints are met before saving file.
433 validate_variables = g_configuration.get(
434 'validate_variables', {})
435 validated_variables = cwrite.verify(
436 cdef, cread, validate_variables)
437 validator = g_configuration.get('validator', noop_validator)
438 validator(validated_variables)
439 cwrite.save(site_config)
441 print "Save failed due to a configuration exception:"
442 print traceback.print_exc()
443 print("Could not save -- fix write access on %s" % site_config)
445 print("Wrote %s" % site_config)
446 consolidate(default_config, site_config, consolidated_config)
447 print("You might want to type 'r' (restart %s), 'R' (reload %s) or 'q' (quit)" %
449 elif command in "uU":
450 global usual_variables
451 global flavour_xml_section_hash
453 for varname in usual_variables:
454 (category, variable) = cdef.locate_varname(varname)
455 if not (category is None and variable is None):
456 prompt_variable(cdef, cread, cwrite,
457 category, variable, False)
459 # set the driver variable according to the already set flavour
460 generic_flavour = cwrite.items('sfa')[0][1]
461 for section in cdef.sections():
462 if generic_flavour in flavour_xml_section_hash and flavour_xml_section_hash[generic_flavour] == section:
463 for item in cdef.items(section):
466 prompt_variable(cdef, cread, cwrite,
467 category, variable, False)
470 except Exception, inst:
471 if (str(inst) != 'BailOut'):
478 print_categories(cread)
479 elif command in "eE":
481 prompt_variables_all(cdef, cread, cwrite, show_comments)
482 elif mode == 'CATEGORY':
483 prompt_variables_category(
484 cdef, cread, cwrite, category_id, show_comments)
485 elif mode == 'VARIABLE':
487 prompt_variable(cdef, cread, cwrite, category, variable,
488 show_comments, False)
489 except Exception, inst:
490 if str(inst) != 'BailOut':
492 elif command in "vVsSlL":
493 show_value = (command in "sSlL")
494 (c1, c2, c3) = (cdef, cread, cwrite)
496 (c1, c2, c3) = (cwrite, cwrite, cwrite)
498 show_variables_all(c1, c2, c3, show_value, show_comments)
499 elif mode == 'CATEGORY':
500 show_variables_category(
501 c1, c2, c3, category_id, show_value, show_comments)
502 elif mode == 'VARIABLE':
503 show_variable(c1, c2, c3, category, variable,
504 show_value, show_comments)
506 print("Unknown command >%s< -- use h for help" % answer)
510 # creates directory for file if not yet existing
511 def check_dir(config_file):
512 dirname = os.path.dirname(config_file)
513 if (not os.path.exists(dirname)):
515 os.makedirs(dirname, 0755)
517 print "Cannot create dir %s due to %s - exiting" % (dirname, e)
520 if (not os.path.exists(dirname)):
521 print "Cannot create dir %s - exiting" % dirname
524 print "Created directory %s" % dirname
529 def optParserSetup(configuration):
530 parser = OptionParser(usage=usage(), version="%prog " + version_tag)
531 parser.set_defaults(config_dir=configuration['config_dir'],
532 service=configuration['service'],
533 usual_variables=configuration['usual_variables'])
534 parser.add_option("", "--configdir", dest="config_dir",
535 help="specify configuration directory")
536 parser.add_option("", "--service", dest="service",
537 help="specify /etc/init.d style service name")
538 parser.add_option("", "--usual_variable", dest="usual_variables",
539 action="append", help="add a usual variable")
543 def main(command, argv, configuration):
544 global g_configuration
545 g_configuration = configuration
547 parser = optParserSetup(configuration)
548 (config, args) = parser.parse_args()
550 parser.error("too many arguments")
552 configuration['service'] = config.service
553 configuration['usual_variables'] = config.usual_variables
554 configuration['config_dir'] = config.config_dir
555 # add in new usual_variables defined on the command line
556 for usual_variable in config.usual_variables:
557 if usual_variable not in configuration['usual_variables']:
558 configuration['usual_variables'].append(usual_variable)
560 # intialize configuration
563 default_config, site_config, consolidated_config = \
564 def_default_config, def_site_config, def_consolidated_config
566 default_config = args[0]
568 site_config = args[1]
570 consolidated_config = args[2]
572 for c in (default_config, site_config, consolidated_config):
576 # the default settings only - read only
577 cdef = Config(default_config)
579 # in effect : default settings + local settings - read only
580 cread = Config(default_config)
582 print traceback.print_exc()
583 print("default config files %s not found, is myplc installed ?" %
587 # local settings only, will be modified & saved
588 config_filename = "%s/sfa_config" % config.config_dir
589 cwrite = Config(config_filename)
591 cread.load(site_config)
592 cwrite.load(default_config)
593 cwrite.load(site_config)
597 mainloop(cdef, cread, cwrite, default_config,
598 site_config, consolidated_config)
601 if __name__ == '__main__':
602 command = sys.argv[0]
604 main(command, argv, configuration)