8 from optparse import OptionParser
10 from sfa.util.version import version_tag
11 from sfa.util.config import Config
14 def validator(validated_variables):
16 # maint_user = validated_variables["PLC_API_MAINTENANCE_USER"]
17 # root_user = validated_variables["PLC_ROOT_USER"]
18 # if maint_user == root_user:
19 # errStr="PLC_API_MAINTENANCE_USER=%s cannot be the same as PLC_ROOT_USER=%s"%(maint_user, root_user)
20 # raise plc_config.ConfigurationException(errStr)
23 "SFA_GENERIC_FLAVOUR",
25 "SFA_REGISTRY_ROOT_AUTH",
31 flavour_xml_section_hash = {
38 'usual_variables': usual_variables,
39 'config_dir': "/etc/sfa",
40 'validate_variables': {},
41 'validator': validator,
47 g_configuration = None
48 usual_variables = None
53 def noop_validator(validated_variables):
56 # historically we could also configure the devel pkg....
59 def init_configuration():
60 global g_configuration
61 global usual_variables, config_dir, service
63 usual_variables = g_configuration["usual_variables"]
64 config_dir = g_configuration["config_dir"]
65 service = g_configuration["service"]
67 global def_default_config, def_site_config, def_consolidated_config
68 def_default_config = "%s/default_config.xml" % config_dir
69 def_site_config = "%s/configs/site_config" % config_dir
70 def_consolidated_config = "%s/%s_config" % (config_dir, service)
73 mainloop_usage = """Available commands:
74 Uppercase versions give variables comments, when available
75 u/U\t\t\tEdit usual variables
77 r\t\t\tRestart %(service)s service
78 R\t\t\tReload %(service)s service (rebuild config files for sh, python....)
79 q\t\t\tQuit (without saving)
82 l/L [<cat>|<var>]\tShow Locally modified variables/values
83 s/S [<cat>|<var>]\tShow variables/values (all, in category, single)
84 e/E [<cat>|<var>]\tEdit variables (all, in category, single)
86 c\t\t\tList categories
87 v/V [<cat>|<var>]\tList Variables (all, in category, single)
89 Typical usage involves: u, [l,] w, r, q
94 command_usage = "%prog [options] [default-xml [site-xml [consolidated-xml]]]"
97 \t default-xml defaults to %s
98 \t site-xml defaults to %s
99 \t consolidated-xml defaults to %s""" % (def_default_config, def_site_config, def_consolidated_config)
103 variable_usage = """Edit Commands :
104 #\tShow variable comments
105 .\tStops prompting, return to mainloop
106 /\tCleans any site-defined value, reverts to default
107 =\tShows default value
108 >\tSkips to next category
115 def get_value(config, category_id, variable_id):
116 value = config.get(category_id, variable_id)
120 def get_type(config, category_id, variable_id):
121 value = config.get(category_id, variable_id)
122 # return variable['type']
126 def get_current_value(cread, cwrite, category_id, variable_id):
127 # the value stored in cwrite, if present, is the one we want
129 result = get_value(cwrite, category_id, variable_id)
131 result = get_value(cread, category_id, variable_id)
134 # refrain from using plc_config's _sanitize
137 def get_varname(config, category_id, variable_id):
138 varname = category_id + "_" + variable_id
139 config.locate_varname(varname)
142 # could not avoid using _sanitize here..
145 def get_name_comments(config, cid, vid):
147 (category, variable) = config.get(cid, vid)
148 (id, name, value, comments) = config._sanitize_variable(cid, variable)
149 return (name, comments)
154 def print_name_comments(config, cid, vid):
155 name, comments = get_name_comments(config, cid, vid)
157 print("### %s" % name)
159 for line in comments:
162 print("!!! No comment associated to %s_%s" % (cid, vid))
167 def list_categories(config):
169 for section in config.sections():
174 def print_categories(config):
175 print("Known categories")
176 for cid in list_categories(config):
177 print("%s" % (cid.upper()))
182 def list_category(config, cid):
184 for section in config.sections():
185 if section == cid.lower():
186 for (name, value) in config.items(section):
187 result += ["%s_%s" % (cid, name)]
191 def print_category(config, cid, show_comments=True):
194 vids = list_category(config, cid)
196 print("%s : no such category" % CID)
198 print("Category %s contains" % (CID))
205 def consolidate(default_config, site_config, consolidated_config):
208 conso = Config(default_config)
209 conso.load(site_config)
210 conso.save(consolidated_config)
211 except Exception as inst:
212 print("Could not consolidate, %s" % (str(inst)))
214 print(("Merged\n\t%s\nand\t%s\ninto\t%s" % (default_config, site_config,
215 consolidated_config)))
218 def reload_service():
219 reload = "sfa-setup.sh reload"
220 print(("Running: {}".format(reload)))
226 def restart_service():
227 services = ('sfa-db', 'sfa-aggregate', 'sfa-registry')
228 for service in services:
229 restart = ("systemctl -q is-active {s} && "
230 "{{ echo restarting {s} ; systemctl restart {s}; }}"
237 def prompt_variable(cdef, cread, cwrite, category, variable,
238 show_comments, support_next=False):
240 category_id = category
241 variable_id = variable
244 default_value = get_value(cdef, category_id, variable_id)
245 variable_type = get_type(cdef, category_id, variable_id)
246 current_value = get_current_value(
247 cread, cwrite, category_id, variable_id)
248 varname = get_varname(cread, category_id, variable_id)
251 print_name_comments(cdef, category_id, variable_id)
252 prompt = "== %s : [%s] " % (varname, current_value)
254 answer = input(prompt).strip()
256 raise Exception('BailOut')
257 except KeyboardInterrupt:
259 raise Exception('BailOut')
262 if (answer == "") or (answer == current_value):
264 elif (answer == "."):
265 raise Exception('BailOut')
266 elif (answer == "#"):
267 print_name_comments(cread, category_id, variable_id)
268 elif (answer == "?"):
269 print(variable_usage.strip())
270 elif (answer == "="):
271 print(("%s defaults to %s" % (varname, default_value)))
272 # revert to default : remove from cwrite (i.e. site-config)
273 elif (answer == "/"):
274 cwrite.delete(category_id, variable_id)
275 print(("%s reverted to %s" % (varname, default_value)))
277 elif (answer == ">"):
279 raise Exception('NextCategory')
281 print("No support for next category")
283 if cdef.validate_type(variable_type, answer):
284 cwrite.set(category_id, variable_id, answer)
287 print("Not a valid value")
290 def prompt_variables_all(cdef, cread, cwrite, show_comments):
292 for (category_id, (category, variables)) in cread.variables().items():
293 print(("========== Category = %s" % category_id.upper()))
294 for variable in list(variables.values()):
296 newvar = prompt_variable(cdef, cread, cwrite, category, variable,
298 except Exception as inst:
299 if (str(inst) == 'NextCategory'):
304 except Exception as inst:
305 if (str(inst) == 'BailOut'):
311 def prompt_variables_category(cdef, cread, cwrite, cid, show_comments):
315 print(("========== Category = %s" % CID))
316 for vid in list_category(cdef, cid):
317 (category, variable) = cdef.locate_varname(vid.upper())
318 newvar = prompt_variable(cdef, cread, cwrite, category, variable,
319 show_comments, False)
320 except Exception as inst:
321 if (str(inst) == 'BailOut'):
329 def show_variable(cdef, cread, cwrite,
330 category, variable, show_value, show_comments):
331 assert 'id' in category
332 assert 'id' in variable
334 category_id = category['id']
335 variable_id = variable['id']
337 default_value = get_value(cdef, category_id, variable_id)
338 current_value = get_current_value(cread, cwrite, category_id, variable_id)
339 varname = get_varname(cread, category_id, variable_id)
341 print_name_comments(cdef, category_id, variable_id)
343 print("%s = %s" % (varname, current_value))
345 print("%s" % (varname))
348 def show_variables_all(cdef, cread, cwrite, show_value, show_comments):
349 for (category_id, (category, variables)) in cread.variables().items():
350 print(("========== Category = %s" % category_id.upper()))
351 for variable in list(variables.values()):
352 show_variable(cdef, cread, cwrite,
353 category, variable, show_value, show_comments)
356 def show_variables_category(cdef, cread, cwrite, cid, show_value, show_comments):
359 print(("========== Category = %s" % CID))
360 for vid in list_category(cdef, cid):
361 (category, variable) = cdef.locate_varname(vid.upper())
362 show_variable(cdef, cread, cwrite, category, variable,
363 show_value, show_comments)
366 re_mainloop_0arg = "^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
367 re_mainloop_1arg = "^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
368 matcher_mainloop_0arg = re.compile(re_mainloop_0arg)
369 matcher_mainloop_1arg = re.compile(re_mainloop_1arg)
372 def mainloop(cdef, cread, cwrite, default_config, site_config, consolidated_config):
377 "Enter command (u for usual changes, w to save, ? for help) ").strip()
380 except KeyboardInterrupt:
384 if (answer == "") or (answer in "?hH"):
385 print(mainloop_usage)
387 groups_parse = matcher_mainloop_0arg.match(answer)
390 command = groups_parse.group('command')
393 groups_parse = matcher_mainloop_1arg.match(answer)
395 command = groups_parse.group('command')
396 arg = groups_parse.group('arg')
398 print(("Unknown command >%s< -- use h for help" % answer))
401 show_comments = command.isupper()
407 variables = list_category(cdef, arg)
409 # category_id as the category name
410 # variables as the list of variable names
414 (category, variable) = cdef.locate_varname(arg)
416 # category/variable as output by locate_varname
419 print("%s: no such category or variable" % arg)
423 # todo check confirmation
427 # Confirm that various constraints are met before saving file.
428 validate_variables = g_configuration.get(
429 'validate_variables', {})
430 validated_variables = cwrite.verify(
431 cdef, cread, validate_variables)
432 validator = g_configuration.get('validator', noop_validator)
433 validator(validated_variables)
434 cwrite.save(site_config)
436 print("Save failed due to a configuration exception:")
437 print(traceback.print_exc())
438 print(("Could not save -- fix write access on %s" % site_config))
440 print(("Wrote %s" % site_config))
441 consolidate(default_config, site_config, consolidated_config)
442 print(("You might want to type 'r' (restart %s), 'R' (reload %s) or 'q' (quit)" %
444 elif command in "uU":
445 global usual_variables
446 global flavour_xml_section_hash
448 for varname in usual_variables:
449 (category, variable) = cdef.locate_varname(varname)
450 if not (category is None and variable is None):
451 prompt_variable(cdef, cread, cwrite,
452 category, variable, False)
454 # set the driver variable according to the already set flavour
455 generic_flavour = cwrite.items('sfa')[0][1]
456 for section in cdef.sections():
457 if generic_flavour in flavour_xml_section_hash and flavour_xml_section_hash[generic_flavour] == section:
458 for item in cdef.items(section):
461 prompt_variable(cdef, cread, cwrite,
462 category, variable, False)
465 except Exception as inst:
466 if (str(inst) != 'BailOut'):
473 print_categories(cread)
474 elif command in "eE":
476 prompt_variables_all(cdef, cread, cwrite, show_comments)
477 elif mode == 'CATEGORY':
478 prompt_variables_category(
479 cdef, cread, cwrite, category_id, show_comments)
480 elif mode == 'VARIABLE':
482 prompt_variable(cdef, cread, cwrite, category, variable,
483 show_comments, False)
484 except Exception as inst:
485 if str(inst) != 'BailOut':
487 elif command in "vVsSlL":
488 show_value = (command in "sSlL")
489 (c1, c2, c3) = (cdef, cread, cwrite)
491 (c1, c2, c3) = (cwrite, cwrite, cwrite)
493 show_variables_all(c1, c2, c3, show_value, show_comments)
494 elif mode == 'CATEGORY':
495 show_variables_category(
496 c1, c2, c3, category_id, show_value, show_comments)
497 elif mode == 'VARIABLE':
498 show_variable(c1, c2, c3, category, variable,
499 show_value, show_comments)
501 print(("Unknown command >%s< -- use h for help" % answer))
505 # creates directory for file if not yet existing
506 def check_dir(config_file):
507 dirname = os.path.dirname(config_file)
508 if (not os.path.exists(dirname)):
510 os.makedirs(dirname, 0o755)
512 print("Cannot create dir %s due to %s - exiting" % (dirname, e))
515 if (not os.path.exists(dirname)):
516 print("Cannot create dir %s - exiting" % dirname)
519 print("Created directory %s" % dirname)
524 def optParserSetup(configuration):
525 parser = OptionParser(usage=usage(), version="%prog " + version_tag)
526 parser.set_defaults(config_dir=configuration['config_dir'],
527 service=configuration['service'],
528 usual_variables=configuration['usual_variables'])
529 parser.add_option("", "--configdir", dest="config_dir",
530 help="specify configuration directory")
531 parser.add_option("", "--service", dest="service",
532 help="specify /etc/init.d style service name")
533 parser.add_option("", "--usual_variable", dest="usual_variables",
534 action="append", help="add a usual variable")
538 def main(command, argv, configuration):
539 global g_configuration
540 g_configuration = configuration
542 parser = optParserSetup(configuration)
543 (config, args) = parser.parse_args()
545 parser.error("too many arguments")
547 configuration['service'] = config.service
548 configuration['usual_variables'] = config.usual_variables
549 configuration['config_dir'] = config.config_dir
550 # add in new usual_variables defined on the command line
551 for usual_variable in config.usual_variables:
552 if usual_variable not in configuration['usual_variables']:
553 configuration['usual_variables'].append(usual_variable)
555 # intialize configuration
558 default_config, site_config, consolidated_config = \
559 def_default_config, def_site_config, def_consolidated_config
561 default_config = args[0]
563 site_config = args[1]
565 consolidated_config = args[2]
567 for c in (default_config, site_config, consolidated_config):
571 # the default settings only - read only
572 cdef = Config(default_config)
574 # in effect : default settings + local settings - read only
575 cread = Config(default_config)
577 print(traceback.print_exc())
578 print(("default config files %s not found, is myplc installed ?" %
582 # local settings only, will be modified & saved
583 config_filename = "%s/sfa_config" % config.config_dir
584 cwrite = Config(config_filename)
586 cread.load(site_config)
587 cwrite.load(default_config)
588 cwrite.load(site_config)
592 mainloop(cdef, cread, cwrite, default_config,
593 site_config, consolidated_config)
596 if __name__ == '__main__':
597 command = sys.argv[0]
599 main(command, argv, configuration)