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():
226 reload = "sfa-setup.sh reload"
227 print("Running: {}".format(reload))
233 def restart_service():
234 services = ('sfa-db', 'sfa-aggregate', 'sfa-registry')
235 for service in services:
236 restart = ("systemctl -q is-active {s} && {{ echo restarting {s} ; systemctl restart {s}; }}"
243 def prompt_variable(cdef, cread, cwrite, category, variable,
244 show_comments, support_next=False):
246 category_id = category
247 variable_id = variable
250 default_value = get_value(cdef, category_id, variable_id)
251 variable_type = get_type(cdef, category_id, variable_id)
252 current_value = get_current_value(
253 cread, cwrite, category_id, variable_id)
254 varname = get_varname(cread, category_id, variable_id)
257 print_name_comments(cdef, category_id, variable_id)
258 prompt = "== %s : [%s] " % (varname, current_value)
260 answer = raw_input(prompt).strip()
262 raise Exception('BailOut')
263 except KeyboardInterrupt:
265 raise Exception('BailOut')
268 if (answer == "") or (answer == current_value):
270 elif (answer == "."):
271 raise Exception('BailOut')
272 elif (answer == "#"):
273 print_name_comments(cread, category_id, variable_id)
274 elif (answer == "?"):
275 print variable_usage.strip()
276 elif (answer == "="):
277 print("%s defaults to %s" % (varname, default_value))
278 # revert to default : remove from cwrite (i.e. site-config)
279 elif (answer == "/"):
280 cwrite.delete(category_id, variable_id)
281 print("%s reverted to %s" % (varname, default_value))
283 elif (answer == ">"):
285 raise Exception('NextCategory')
287 print "No support for next category"
289 if cdef.validate_type(variable_type, answer):
290 cwrite.set(category_id, variable_id, answer)
293 print "Not a valid value"
296 def prompt_variables_all(cdef, cread, cwrite, show_comments):
298 for (category_id, (category, variables)) in cread.variables().iteritems():
299 print("========== Category = %s" % category_id.upper())
300 for variable in variables.values():
302 newvar = prompt_variable(cdef, cread, cwrite, category, variable,
304 except Exception, inst:
305 if (str(inst) == 'NextCategory'):
310 except Exception, inst:
311 if (str(inst) == 'BailOut'):
317 def prompt_variables_category(cdef, cread, cwrite, cid, show_comments):
321 print("========== Category = %s" % CID)
322 for vid in list_category(cdef, cid):
323 (category, variable) = cdef.locate_varname(vid.upper())
324 newvar = prompt_variable(cdef, cread, cwrite, category, variable,
325 show_comments, False)
326 except Exception, inst:
327 if (str(inst) == 'BailOut'):
335 def show_variable(cdef, cread, cwrite,
336 category, variable, show_value, show_comments):
337 assert category.has_key('id')
338 assert variable.has_key('id')
340 category_id = category['id']
341 variable_id = variable['id']
343 default_value = get_value(cdef, category_id, variable_id)
344 current_value = get_current_value(cread, cwrite, category_id, variable_id)
345 varname = get_varname(cread, category_id, variable_id)
347 print_name_comments(cdef, category_id, variable_id)
349 print "%s = %s" % (varname, current_value)
351 print "%s" % (varname)
354 def show_variables_all(cdef, cread, cwrite, show_value, show_comments):
355 for (category_id, (category, variables)) in cread.variables().iteritems():
356 print("========== Category = %s" % category_id.upper())
357 for variable in variables.values():
358 show_variable(cdef, cread, cwrite,
359 category, variable, show_value, show_comments)
362 def show_variables_category(cdef, cread, cwrite, cid, show_value, show_comments):
365 print("========== Category = %s" % CID)
366 for vid in list_category(cdef, cid):
367 (category, variable) = cdef.locate_varname(vid.upper())
368 show_variable(cdef, cread, cwrite, category, variable,
369 show_value, show_comments)
372 re_mainloop_0arg = "^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
373 re_mainloop_1arg = "^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
374 matcher_mainloop_0arg = re.compile(re_mainloop_0arg)
375 matcher_mainloop_1arg = re.compile(re_mainloop_1arg)
378 def mainloop(cdef, cread, cwrite, default_config, site_config, consolidated_config):
383 "Enter command (u for usual changes, w to save, ? for help) ").strip()
386 except KeyboardInterrupt:
390 if (answer == "") or (answer in "?hH"):
393 groups_parse = matcher_mainloop_0arg.match(answer)
396 command = groups_parse.group('command')
399 groups_parse = matcher_mainloop_1arg.match(answer)
401 command = groups_parse.group('command')
402 arg = groups_parse.group('arg')
404 print("Unknown command >%s< -- use h for help" % answer)
407 show_comments = command.isupper()
413 variables = list_category(cdef, arg)
415 # category_id as the category name
416 # variables as the list of variable names
420 (category, variable) = cdef.locate_varname(arg)
422 # category/variable as output by locate_varname
425 print "%s: no such category or variable" % arg
429 # todo check confirmation
433 # Confirm that various constraints are met before saving file.
434 validate_variables = g_configuration.get(
435 'validate_variables', {})
436 validated_variables = cwrite.verify(
437 cdef, cread, validate_variables)
438 validator = g_configuration.get('validator', noop_validator)
439 validator(validated_variables)
440 cwrite.save(site_config)
442 print "Save failed due to a configuration exception:"
443 print traceback.print_exc()
444 print("Could not save -- fix write access on %s" % site_config)
446 print("Wrote %s" % site_config)
447 consolidate(default_config, site_config, consolidated_config)
448 print("You might want to type 'r' (restart %s), 'R' (reload %s) or 'q' (quit)" %
450 elif command in "uU":
451 global usual_variables
452 global flavour_xml_section_hash
454 for varname in usual_variables:
455 (category, variable) = cdef.locate_varname(varname)
456 if not (category is None and variable is None):
457 prompt_variable(cdef, cread, cwrite,
458 category, variable, False)
460 # set the driver variable according to the already set flavour
461 generic_flavour = cwrite.items('sfa')[0][1]
462 for section in cdef.sections():
463 if generic_flavour in flavour_xml_section_hash and flavour_xml_section_hash[generic_flavour] == section:
464 for item in cdef.items(section):
467 prompt_variable(cdef, cread, cwrite,
468 category, variable, False)
471 except Exception, inst:
472 if (str(inst) != 'BailOut'):
479 print_categories(cread)
480 elif command in "eE":
482 prompt_variables_all(cdef, cread, cwrite, show_comments)
483 elif mode == 'CATEGORY':
484 prompt_variables_category(
485 cdef, cread, cwrite, category_id, show_comments)
486 elif mode == 'VARIABLE':
488 prompt_variable(cdef, cread, cwrite, category, variable,
489 show_comments, False)
490 except Exception, inst:
491 if str(inst) != 'BailOut':
493 elif command in "vVsSlL":
494 show_value = (command in "sSlL")
495 (c1, c2, c3) = (cdef, cread, cwrite)
497 (c1, c2, c3) = (cwrite, cwrite, cwrite)
499 show_variables_all(c1, c2, c3, show_value, show_comments)
500 elif mode == 'CATEGORY':
501 show_variables_category(
502 c1, c2, c3, category_id, show_value, show_comments)
503 elif mode == 'VARIABLE':
504 show_variable(c1, c2, c3, category, variable,
505 show_value, show_comments)
507 print("Unknown command >%s< -- use h for help" % answer)
511 # creates directory for file if not yet existing
512 def check_dir(config_file):
513 dirname = os.path.dirname(config_file)
514 if (not os.path.exists(dirname)):
516 os.makedirs(dirname, 0755)
518 print "Cannot create dir %s due to %s - exiting" % (dirname, e)
521 if (not os.path.exists(dirname)):
522 print "Cannot create dir %s - exiting" % dirname
525 print "Created directory %s" % dirname
530 def optParserSetup(configuration):
531 parser = OptionParser(usage=usage(), version="%prog " + version_tag)
532 parser.set_defaults(config_dir=configuration['config_dir'],
533 service=configuration['service'],
534 usual_variables=configuration['usual_variables'])
535 parser.add_option("", "--configdir", dest="config_dir",
536 help="specify configuration directory")
537 parser.add_option("", "--service", dest="service",
538 help="specify /etc/init.d style service name")
539 parser.add_option("", "--usual_variable", dest="usual_variables",
540 action="append", help="add a usual variable")
544 def main(command, argv, configuration):
545 global g_configuration
546 g_configuration = configuration
548 parser = optParserSetup(configuration)
549 (config, args) = parser.parse_args()
551 parser.error("too many arguments")
553 configuration['service'] = config.service
554 configuration['usual_variables'] = config.usual_variables
555 configuration['config_dir'] = config.config_dir
556 # add in new usual_variables defined on the command line
557 for usual_variable in config.usual_variables:
558 if usual_variable not in configuration['usual_variables']:
559 configuration['usual_variables'].append(usual_variable)
561 # intialize configuration
564 default_config, site_config, consolidated_config = \
565 def_default_config, def_site_config, def_consolidated_config
567 default_config = args[0]
569 site_config = args[1]
571 consolidated_config = args[2]
573 for c in (default_config, site_config, consolidated_config):
577 # the default settings only - read only
578 cdef = Config(default_config)
580 # in effect : default settings + local settings - read only
581 cread = Config(default_config)
583 print traceback.print_exc()
584 print("default config files %s not found, is myplc installed ?" %
588 # local settings only, will be modified & saved
589 config_filename = "%s/sfa_config" % config.config_dir
590 cwrite = Config(config_filename)
592 cread.load(site_config)
593 cwrite.load(default_config)
594 cwrite.load(site_config)
598 mainloop(cdef, cread, cwrite, default_config,
599 site_config, consolidated_config)
602 if __name__ == '__main__':
603 command = sys.argv[0]
605 main(command, argv, configuration)