the big cleanup: remove unused code relating to openstack/nova
[sfa.git] / config / sfa-config-tty
1 #!/usr/bin/env python
2
3 import os
4 import sys
5 import re
6 import time
7 import traceback
8 import types
9 import readline
10 from StringIO import StringIO
11 from optparse import OptionParser
12
13 from sfa.util.version import version_tag
14 from sfa.util.config import Config
15
16
17 def validator(validated_variables):
18     pass
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)
24
25 usual_variables = [
26     "SFA_GENERIC_FLAVOUR",
27     "SFA_INTERFACE_HRN",
28     "SFA_REGISTRY_ROOT_AUTH",
29     "SFA_REGISTRY_HOST",
30     "SFA_AGGREGATE_HOST",
31     "SFA_SM_HOST",
32     "SFA_DB_HOST",
33 ]
34
35 flavour_xml_section_hash = {
36     'pl': 'sfa_plc',
37     'nitos': 'sfa_nitos',
38     'dummy': 'sfa_dummy',
39 }
40 configuration = {
41     'name': 'sfa',
42     'service': "sfa",
43     'usual_variables': usual_variables,
44     'config_dir': "/etc/sfa",
45     'validate_variables': {},
46     'validator': validator,
47 }
48
49
50 # GLOBAL VARIABLES
51 #
52 g_configuration = None
53 usual_variables = None
54 config_dir = None
55 service = None
56
57
58 def noop_validator(validated_variables):
59     pass
60
61 # historically we could also configure the devel pkg....
62
63
64 def init_configuration():
65     global g_configuration
66     global usual_variables, config_dir, service
67
68     usual_variables = g_configuration["usual_variables"]
69     config_dir = g_configuration["config_dir"]
70     service = g_configuration["service"]
71
72     global def_default_config, def_site_config, def_consolidated_config
73     def_default_config = "%s/default_config.xml" % config_dir
74     def_site_config = "%s/configs/site_config" % config_dir
75     def_consolidated_config = "%s/%s_config" % (config_dir, service)
76
77     global mainloop_usage
78     mainloop_usage = """Available commands:
79  Uppercase versions give variables comments, when available
80  u/U\t\t\tEdit usual variables
81  w\t\t\tWrite
82  r\t\t\tRestart %(service)s service
83  R\t\t\tReload %(service)s service (rebuild config files for sh, python....)
84  q\t\t\tQuit (without saving)
85  h/?\t\t\tThis help
86 ---
87  l/L [<cat>|<var>]\tShow Locally modified variables/values
88  s/S [<cat>|<var>]\tShow variables/values (all, in category, single)
89  e/E [<cat>|<var>]\tEdit variables (all, in category, single)
90 ---
91  c\t\t\tList categories
92  v/V [<cat>|<var>]\tList Variables (all, in category, single)
93 ---
94 Typical usage involves: u, [l,] w, r, q
95 """ % globals()
96
97
98 def usage():
99     command_usage = "%prog [options] [default-xml [site-xml [consolidated-xml]]]"
100     init_configuration()
101     command_usage += """
102 \t default-xml defaults to %s
103 \t site-xml defaults to %s
104 \t consolidated-xml defaults to %s""" % (def_default_config, def_site_config, def_consolidated_config)
105     return command_usage
106
107 ####################
108 variable_usage = """Edit Commands :
109 #\tShow variable comments
110 .\tStops prompting, return to mainloop
111 /\tCleans any site-defined value, reverts to default
112 =\tShows default value
113 >\tSkips to next category
114 ?\tThis help
115 """
116
117 ####################
118
119
120 def get_value(config,  category_id, variable_id):
121     value = config.get(category_id, variable_id)
122     return value
123
124
125 def get_type(config, category_id, variable_id):
126     value = config.get(category_id, variable_id)
127     # return variable['type']
128     return str
129
130
131 def get_current_value(cread, cwrite, category_id, variable_id):
132     # the value stored in cwrite, if present, is the one we want
133     try:
134         result = get_value(cwrite, category_id, variable_id)
135     except:
136         result = get_value(cread, category_id, variable_id)
137     return result
138
139 # refrain from using plc_config's _sanitize
140
141
142 def get_varname(config,  category_id, variable_id):
143     varname = category_id + "_" + variable_id
144     config.locate_varname(varname)
145     return varname
146
147 # could not avoid using _sanitize here..
148
149
150 def get_name_comments(config, cid, vid):
151     try:
152         (category, variable) = config.get(cid, vid)
153         (id, name, value, comments) = config._sanitize_variable(cid, variable)
154         return (name, comments)
155     except:
156         return (None, [])
157
158
159 def print_name_comments(config, cid, vid):
160     name, comments = get_name_comments(config, cid, vid)
161     if name:
162         print "### %s" % name
163     if comments:
164         for line in comments:
165             print "# %s" % line
166     else:
167         print "!!! No comment associated to %s_%s" % (cid, vid)
168
169 ####################
170
171
172 def list_categories(config):
173     result = []
174     for section in config.sections():
175         result += [section]
176     return result
177
178
179 def print_categories(config):
180     print "Known categories"
181     for cid in list_categories(config):
182         print "%s" % (cid.upper())
183
184 ####################
185
186
187 def list_category(config, cid):
188     result = []
189     for section in config.sections():
190         if section == cid.lower():
191             for (name, value) in config.items(section):
192                 result += ["%s_%s" % (cid, name)]
193     return result
194
195
196 def print_category(config, cid, show_comments=True):
197     cid = cid.lower()
198     CID = cid.upper()
199     vids = list_category(config, cid)
200     if (len(vids) == 0):
201         print "%s : no such category" % CID
202     else:
203         print "Category %s contains" % (CID)
204         for vid in vids:
205             print vid.upper()
206
207 ####################
208
209
210 def consolidate(default_config, site_config, consolidated_config):
211     global service
212     try:
213         conso = Config(default_config)
214         conso.load(site_config)
215         conso.save(consolidated_config)
216     except Exception, inst:
217         print "Could not consolidate, %s" % (str(inst))
218         return
219     print("Merged\n\t%s\nand\t%s\ninto\t%s" % (default_config, site_config,
220                                                consolidated_config))
221
222
223 def reload_service():
224     reload = "sfa-setup.sh reload"
225     print("Running: {}".format(reload))
226     os.system(reload)
227
228 ####################
229
230
231 def restart_service():
232     services = ('sfa-db', 'sfa-aggregate', 'sfa-registry')
233     for service in services:
234         restart = ("systemctl -q is-active {s} && {{ echo restarting {s} ; systemctl restart {s}; }}"
235                    .format(s=service))
236         os.system(restart)
237
238 ####################
239
240
241 def prompt_variable(cdef, cread, cwrite, category, variable,
242                     show_comments, support_next=False):
243
244     category_id = category
245     variable_id = variable
246
247     while True:
248         default_value = get_value(cdef, category_id, variable_id)
249         variable_type = get_type(cdef, category_id, variable_id)
250         current_value = get_current_value(
251             cread, cwrite, category_id, variable_id)
252         varname = get_varname(cread, category_id, variable_id)
253
254         if show_comments:
255             print_name_comments(cdef, category_id, variable_id)
256         prompt = "== %s : [%s] " % (varname, current_value)
257         try:
258             answer = raw_input(prompt).strip()
259         except EOFError:
260             raise Exception('BailOut')
261         except KeyboardInterrupt:
262             print "\n"
263             raise Exception('BailOut')
264
265         # no change
266         if (answer == "") or (answer == current_value):
267             return None
268         elif (answer == "."):
269             raise Exception('BailOut')
270         elif (answer == "#"):
271             print_name_comments(cread, category_id, variable_id)
272         elif (answer == "?"):
273             print variable_usage.strip()
274         elif (answer == "="):
275             print("%s defaults to %s" % (varname, default_value))
276         # revert to default : remove from cwrite (i.e. site-config)
277         elif (answer == "/"):
278             cwrite.delete(category_id, variable_id)
279             print("%s reverted to %s" % (varname, default_value))
280             return
281         elif (answer == ">"):
282             if support_next:
283                 raise Exception('NextCategory')
284             else:
285                 print "No support for next category"
286         else:
287             if cdef.validate_type(variable_type, answer):
288                 cwrite.set(category_id, variable_id, answer)
289                 return
290             else:
291                 print "Not a valid value"
292
293
294 def prompt_variables_all(cdef, cread, cwrite, show_comments):
295     try:
296         for (category_id, (category, variables)) in cread.variables().iteritems():
297             print("========== Category = %s" % category_id.upper())
298             for variable in variables.values():
299                 try:
300                     newvar = prompt_variable(cdef, cread, cwrite, category, variable,
301                                              show_comments, True)
302                 except Exception, inst:
303                     if (str(inst) == 'NextCategory'):
304                         break
305                     else:
306                         raise
307
308     except Exception, inst:
309         if (str(inst) == 'BailOut'):
310             return
311         else:
312             raise
313
314
315 def prompt_variables_category(cdef, cread, cwrite, cid, show_comments):
316     cid = cid.lower()
317     CID = cid.upper()
318     try:
319         print("========== Category = %s" % CID)
320         for vid in list_category(cdef, cid):
321             (category, variable) = cdef.locate_varname(vid.upper())
322             newvar = prompt_variable(cdef, cread, cwrite, category, variable,
323                                      show_comments, False)
324     except Exception, inst:
325         if (str(inst) == 'BailOut'):
326             return
327         else:
328             raise
329
330 ####################
331
332
333 def show_variable(cdef, cread, cwrite,
334                   category, variable, show_value, show_comments):
335     assert category.has_key('id')
336     assert variable.has_key('id')
337
338     category_id = category['id']
339     variable_id = variable['id']
340
341     default_value = get_value(cdef, category_id, variable_id)
342     current_value = get_current_value(cread, cwrite, category_id, variable_id)
343     varname = get_varname(cread, category_id, variable_id)
344     if show_comments:
345         print_name_comments(cdef, category_id, variable_id)
346     if show_value:
347         print "%s = %s" % (varname, current_value)
348     else:
349         print "%s" % (varname)
350
351
352 def show_variables_all(cdef, cread, cwrite, show_value, show_comments):
353     for (category_id, (category, variables)) in cread.variables().iteritems():
354         print("========== Category = %s" % category_id.upper())
355         for variable in variables.values():
356             show_variable(cdef, cread, cwrite,
357                           category, variable, show_value, show_comments)
358
359
360 def show_variables_category(cdef, cread, cwrite, cid, show_value, show_comments):
361     cid = cid.lower()
362     CID = cid.upper()
363     print("========== Category = %s" % CID)
364     for vid in list_category(cdef, cid):
365         (category, variable) = cdef.locate_varname(vid.upper())
366         show_variable(cdef, cread, cwrite, category, variable,
367                       show_value, show_comments)
368
369 ####################
370 re_mainloop_0arg = "^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
371 re_mainloop_1arg = "^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
372 matcher_mainloop_0arg = re.compile(re_mainloop_0arg)
373 matcher_mainloop_1arg = re.compile(re_mainloop_1arg)
374
375
376 def mainloop(cdef, cread, cwrite, default_config, site_config, consolidated_config):
377     global service
378     while True:
379         try:
380             answer = raw_input(
381                 "Enter command (u for usual changes, w to save, ? for help) ").strip()
382         except EOFError:
383             answer = ""
384         except KeyboardInterrupt:
385             print "\nBye"
386             sys.exit()
387
388         if (answer == "") or (answer in "?hH"):
389             print mainloop_usage
390             continue
391         groups_parse = matcher_mainloop_0arg.match(answer)
392         command = None
393         if (groups_parse):
394             command = groups_parse.group('command')
395             arg = None
396         else:
397             groups_parse = matcher_mainloop_1arg.match(answer)
398             if (groups_parse):
399                 command = groups_parse.group('command')
400                 arg = groups_parse.group('arg')
401         if not command:
402             print("Unknown command >%s< -- use h for help" % answer)
403             continue
404
405         show_comments = command.isupper()
406
407         mode = 'ALL'
408         if arg:
409             mode = None
410             arg = arg.lower()
411             variables = list_category(cdef, arg)
412             if len(variables):
413                 # category_id as the category name
414                 # variables as the list of variable names
415                 mode = 'CATEGORY'
416                 category_id = arg
417             arg = arg.upper()
418             (category, variable) = cdef.locate_varname(arg)
419             if variable:
420                 # category/variable as output by locate_varname
421                 mode = 'VARIABLE'
422             if not mode:
423                 print "%s: no such category or variable" % arg
424                 continue
425
426         if command in "qQ":
427             # todo check confirmation
428             return
429         elif command == "w":
430             try:
431                 # Confirm that various constraints are met before saving file.
432                 validate_variables = g_configuration.get(
433                     'validate_variables', {})
434                 validated_variables = cwrite.verify(
435                     cdef, cread, validate_variables)
436                 validator = g_configuration.get('validator', noop_validator)
437                 validator(validated_variables)
438                 cwrite.save(site_config)
439             except:
440                 print "Save failed due to a configuration exception:"
441                 print traceback.print_exc()
442                 print("Could not save -- fix write access on %s" % site_config)
443                 break
444             print("Wrote %s" % site_config)
445             consolidate(default_config, site_config, consolidated_config)
446             print("You might want to type 'r' (restart %s), 'R' (reload %s) or 'q' (quit)" %
447                   (service, service))
448         elif command in "uU":
449             global usual_variables
450             global flavour_xml_section_hash
451             try:
452                 for varname in usual_variables:
453                     (category, variable) = cdef.locate_varname(varname)
454                     if not (category is None and variable is None):
455                         prompt_variable(cdef, cread, cwrite,
456                                         category, variable, False)
457
458                 # set the driver variable according to the already set flavour
459                 generic_flavour = cwrite.items('sfa')[0][1]
460                 for section in cdef.sections():
461                     if generic_flavour in flavour_xml_section_hash and flavour_xml_section_hash[generic_flavour] == section:
462                         for item in cdef.items(section):
463                             category = section
464                             variable = item[0]
465                             prompt_variable(cdef, cread, cwrite,
466                                             category, variable, False)
467                         break
468
469             except Exception, inst:
470                 if (str(inst) != 'BailOut'):
471                     raise
472         elif command == "r":
473             restart_service()
474         elif command == "R":
475             reload_service()
476         elif command == "c":
477             print_categories(cread)
478         elif command in "eE":
479             if mode == 'ALL':
480                 prompt_variables_all(cdef, cread, cwrite, show_comments)
481             elif mode == 'CATEGORY':
482                 prompt_variables_category(
483                     cdef, cread, cwrite, category_id, show_comments)
484             elif mode == 'VARIABLE':
485                 try:
486                     prompt_variable(cdef, cread, cwrite, category, variable,
487                                     show_comments, False)
488                 except Exception, inst:
489                     if str(inst) != 'BailOut':
490                         raise
491         elif command in "vVsSlL":
492             show_value = (command in "sSlL")
493             (c1, c2, c3) = (cdef, cread, cwrite)
494             if command in "lL":
495                 (c1, c2, c3) = (cwrite, cwrite, cwrite)
496             if mode == 'ALL':
497                 show_variables_all(c1, c2, c3, show_value, show_comments)
498             elif mode == 'CATEGORY':
499                 show_variables_category(
500                     c1, c2, c3, category_id, show_value, show_comments)
501             elif mode == 'VARIABLE':
502                 show_variable(c1, c2, c3, category, variable,
503                               show_value, show_comments)
504         else:
505             print("Unknown command >%s< -- use h for help" % answer)
506
507
508 ####################
509 # creates directory for file if not yet existing
510 def check_dir(config_file):
511     dirname = os.path.dirname(config_file)
512     if (not os.path.exists(dirname)):
513         try:
514             os.makedirs(dirname, 0755)
515         except OSError, e:
516             print "Cannot create dir %s due to %s - exiting" % (dirname, e)
517             sys.exit(1)
518
519         if (not os.path.exists(dirname)):
520             print "Cannot create dir %s - exiting" % dirname
521             sys.exit(1)
522         else:
523             print "Created directory %s" % dirname
524
525 ####################
526
527
528 def optParserSetup(configuration):
529     parser = OptionParser(usage=usage(), version="%prog " + version_tag)
530     parser.set_defaults(config_dir=configuration['config_dir'],
531                         service=configuration['service'],
532                         usual_variables=configuration['usual_variables'])
533     parser.add_option("", "--configdir", dest="config_dir",
534                       help="specify configuration directory")
535     parser.add_option("", "--service", dest="service",
536                       help="specify /etc/init.d style service name")
537     parser.add_option("", "--usual_variable", dest="usual_variables",
538                       action="append", help="add a usual variable")
539     return parser
540
541
542 def main(command, argv, configuration):
543     global g_configuration
544     g_configuration = configuration
545
546     parser = optParserSetup(configuration)
547     (config, args) = parser.parse_args()
548     if len(args) > 3:
549         parser.error("too many arguments")
550
551     configuration['service'] = config.service
552     configuration['usual_variables'] = config.usual_variables
553     configuration['config_dir'] = config.config_dir
554     # add in new usual_variables defined on the command line
555     for usual_variable in config.usual_variables:
556         if usual_variable not in configuration['usual_variables']:
557             configuration['usual_variables'].append(usual_variable)
558
559     # intialize configuration
560     init_configuration()
561
562     default_config, site_config, consolidated_config = \
563         def_default_config, def_site_config, def_consolidated_config
564     if len(args) >= 1:
565         default_config = args[0]
566     if len(args) >= 2:
567         site_config = args[1]
568     if len(args) == 3:
569         consolidated_config = args[2]
570
571     for c in (default_config, site_config, consolidated_config):
572         check_dir(c)
573
574     try:
575         # the default settings only - read only
576         cdef = Config(default_config)
577
578         # in effect : default settings + local settings - read only
579         cread = Config(default_config)
580     except:
581         print traceback.print_exc()
582         print("default config files %s not found, is myplc installed ?" %
583               default_config)
584         return 1
585
586     # local settings only, will be modified & saved
587     config_filename = "%s/sfa_config" % config.config_dir
588     cwrite = Config(config_filename)
589     try:
590         cread.load(site_config)
591         cwrite.load(default_config)
592         cwrite.load(site_config)
593     except:
594         cwrite = Config()
595
596     mainloop(cdef, cread, cwrite, default_config,
597              site_config, consolidated_config)
598     return 0
599
600 if __name__ == '__main__':
601     command = sys.argv[0]
602     argv = sys.argv[1:]
603     main(command, argv, configuration)