- fix bind umount of fedora mirror during bootstrap build
[myplc.git] / plc-config-tty
1 #!/usr/bin/python
2
3 # Interactively prompts for variable values
4 # expected arguments are
5 # command [default-xml [custom-xml]]
6 #
7 # Two-steps logic:
8 # (1) scans all variables (todo: pass categories as arguments)
9 #     and prompts for value
10 #     current value proposed as default
11 #     also allows to remove site-dependent setting
12 # (2) epilogue : allows to
13 #     list the site-dependent vars with values
14 #     and to locally (re-)edit a variable from its shell name
15 #     quit with or without saving
16
17 import sys
18 import os
19 import re
20 import readline
21 import getopt
22
23 from plc_config import PLCConfiguration
24
25 ####################
26 release = "$Id$"
27
28 def init_flavour (flavour):
29     global service
30     global common_variables
31     if (flavour == "devel"):
32         service="plc-devel"
33         common_variables=("PLC_DEVEL_FEDORA_URL",
34                           "PLC_DEVEL_CVSROOT")
35         config_dir = "/plc/devel/data/etc/planetlab"
36     else:
37         service="plc"
38         common_variables=("PLC_NAME",
39                           "PLC_ROOT_USER",
40                           "PLC_ROOT_PASSWORD",
41                           "PLC_MAIL_SUPPORT_ADDRESS",
42                           "PLC_DB_HOST",
43                           "PLC_API_HOST",
44                           "PLC_WWW_HOST",
45                           "PLC_BOOT_HOST",
46                           "PLC_NET_DNS1",
47                           "PLC_NET_DNS2")
48         config_dir = "/etc/planetlab"
49     global def_main_config
50     def_main_config= "%s/default_config.xml" % config_dir
51     global def_site_config
52     def_site_config = "%s/configs/site.xml" % config_dir
53     global def_consolidated_config
54     def_consolidated_config = "%s/plc_config.xml" % config_dir
55
56     global mainloop_usage
57     mainloop_usage= """Available commands
58 c\tEdits commonly tuned variables
59 e\tEdits all variables
60 p\tPrints all locally-customized vars and values
61 e <var>\tPrompts (edit) fro variable <var>
62 p <var>\tShows current setting for <var>
63 l\tlists all known variables
64 w\tsaves & consolidates
65 r\trestarts %s service
66 q\tQuits without saving
67 ---
68 Typical usage involves: c, [p,] w, r
69 """ % service
70
71 def usage ():
72     command_usage="Usage: %s [-d] [-v] [default-xml [site-xml [consolidated-xml]]]"% sys.argv[0]
73     init_flavour ("boot")
74     command_usage +="""
75 \t default-xml defaults to %s
76 \t site-xml defaults to %s
77 \t consolidated-xml defaults to %s""" % (def_main_config,def_site_config, def_consolidated_config)
78     command_usage += """
79   Unless you specify the -d option, meaning you want to configure
80   myplc-devel instead of regular myplc, in which case""" 
81     init_flavour ("devel")
82     command_usage +="""
83 \t default-xml defaults to %s
84 \t site-xml defaults to %s
85 \t consolidated-xml defaults to %s""" % (def_main_config,def_site_config, def_consolidated_config)
86     print(command_usage)
87     sys.exit(1)
88
89 ####################
90 variable_usage= """Special answers :
91 #\tShow variable comments
92 .\tStops prompting, return to mainloop
93 /\tCleans any site-defined value, reverts to default
94 =\tShows default value
95 >\tSkips to next category
96 ?\tThis help
97 """
98
99 ####################
100 def get_value (config,  category_id, variable_id):
101     (category, variable) = config.get (category_id, variable_id)
102     return variable['value']
103
104 # refrain from using plc_config's _sanitize 
105 def get_varname (config,  category_id, variable_id):
106     (category, variable) = config.get (category_id, variable_id)
107     return (category_id+"_"+variable['id']).upper()
108
109 def prompt_variable (cdef, cread, cwrite, category, variable):
110
111     assert category.has_key('id')
112     assert variable.has_key('id')
113
114     category_id = category ['id']
115     variable_id = variable['id']
116
117     while True:
118         default_value = get_value(cdef,category_id,variable_id)
119         current_value = get_value(cread,category_id, variable_id)
120         varname = get_varname (cread,category_id, variable_id)
121         
122         prompt = "== %s : [%s] " % (varname,current_value)
123         try:
124             answer = raw_input(prompt).strip()
125         except EOFError :
126             raise Exception ('BailOut')
127
128         # no change
129         if (answer == "") or (answer == current_value):
130             return None
131         elif (answer == "."):
132             raise Exception ('BailOut')
133         elif (answer == ">"):
134             raise Exception ('NextCategory')
135         elif (answer == "#"):
136             if friendly_name is not None:
137                 print ("### " + friendly_name)
138             if comments == None:
139                 print ("!!! No comment associated to %s" % varname)
140             else:
141                 for line in comments:
142                     print ("# " + line)
143         elif (answer == "?"):
144             print variable_usage.strip()
145         elif (answer == "="):
146             print ("%s defaults to %s" %(varname,default_value))
147         # revert to default : remove from cwrite (i.e. site-config)
148         elif (answer == "/"):
149             cwrite.delete(category_id,variable_id)
150             print ("%s reverted to %s" %(varname,default_value))
151             return
152         else:
153             variable['value'] = answer
154             cwrite.set(category,variable)
155             return
156
157 ####################
158 def prompt_all_variables (cdef, cread, cwrite):
159     try:
160         for (category_id, (category, variables)) in cread.variables().iteritems():
161             print ("========== Category = %s" % category_id)
162             for variable in variables.values():
163                 try:
164                     newvar = prompt_variable (cdef, cread, cwrite, category, variable)
165                 except Exception, inst:
166                     if (str(inst) == 'NextCategory'): break
167                     else: raise
168                     
169     except Exception, inst:
170         if (str(inst) == 'BailOut'): return
171         else: raise
172
173
174 ####################
175 def consolidate (main_config, site_config, consolidated_config):
176     try:
177         conso = PLCConfiguration (main_config)
178         conso.load (site_config)
179         conso.save (consolidated_config)
180     except Exception, inst:
181         print "Could not consolidate, %s" % (str(inst))
182         return
183     print ("Merged\n\t%s\nand\t%s\ninto\t%s"%(main_config,site_config,consolidated_config))
184         
185 ####################
186 def restart_plc ():
187     print ("==================== Stopping %s" % service)
188     os.system("service %s stop" % service)
189     print ("==================== Starting %s" % service)
190     os.system("service %s start" % service)
191
192 ####################
193
194 re_mainloop_var="^(?P<command>[pe])[ \t]+(?P<varname>\w+)$"
195 matcher_mainloop_var=re.compile(re_mainloop_var)
196
197 def mainloop (cdef, cread, cwrite,main_config, site_config, consolidated_config):
198     while True:
199         try:
200             answer = raw_input("Enter command (c for usual changes, w to save, ? for help) ").strip()
201         except EOFError:
202             answer =""
203         answer=answer.lower()
204         if (answer == "") or (answer == "?") or (answer == "h"):
205             print mainloop_usage
206         elif (answer == "q"):
207             # todo check confirmation
208             return
209         elif (answer == "e"):
210             prompt_all_variables(cdef, cread, cwrite)
211         elif (answer == "w"):
212             try:
213                 cwrite.save(site_config)
214             except:
215                 print ("Could not save -- fix write access on %s" % site_config)
216                 break
217             print ("Wrote %s" % site_config)
218             consolidate(main_config, site_config, consolidated_config)
219             print ("You might want to type 'r' (restart plc) or 'q' (quit)")
220         elif (answer == "l"):
221             print ("Config involves the following variables")
222             sys.stdout.write(cread.output_variables())
223         elif (answer == "p"):
224             print ("Current site config")
225             sys.stdout.write(cwrite.output_shell(False))
226         elif (answer == "c"):
227             try:
228                 for varname in common_variables:
229                     (category,variable) = cdef.locate_varname(varname)
230                     prompt_variable(cdef, cread, cwrite, category, variable)
231             except Exception, inst:
232                 if (str(inst) != 'BailOut'):
233                     raise
234         elif (answer == "r"):
235             restart_plc()
236         else:
237             groups_var = matcher_mainloop_var.match(answer)
238             if (groups_var):
239                 command = groups_var.group('command')
240                 varname = groups_var.group('varname')
241                 (category,variable) = cdef.locate_varname(varname)
242                 if not category:
243                     print "Unknown variable %s" % varname
244                 elif (command == 'p'):
245                     print ("%s = %s" % (varname,get_value(cwrite,
246                                                           category['id'],
247                                                           variable['id'])))
248                 else:
249                     try:
250                         prompt_variable(cdef, cread, cwrite, category,variable)
251                     except Exception, inst:
252                         if (str(inst) != 'BailOut'):
253                             raise
254             else:
255                 print ("Unknown command >%s<" % answer)
256
257 ####################
258 def main ():
259
260     command=sys.argv[0]
261     argv = sys.argv[1:]
262
263     save = True
264     # default is myplc (non -devel) unless -d is specified
265     init_flavour("boot")
266     optlist,list = getopt.getopt(argv,":dhv")
267     for opt in optlist:
268         if opt[0] == "-h":
269             usage()
270         if opt[0] == "-v":
271             print ("This is %s - %s" %(command,release))
272             sys.exit(1)
273         if opt[0] == "-d":
274             init_flavour("devel")
275             argv=argv[1:]
276
277     if len(argv) == 0:
278         (main_config,site_config,consolidated_config) = (def_main_config, def_site_config, def_consolidated_config)
279     elif len(argv) == 1:
280         (main_config,site_config,consolidated_config) = (argv[0], def_site_config, def_consolidated_config)
281     elif len(argv) == 2:
282         (main_config, site_config,consolidated_config)  = (argv[0], argv[1], def_consolidated_config)
283     elif len(argv) == 3:
284         (main_config, site_config,consolidated_config)  = argv
285     else:
286         usage()
287
288     try:
289         # the default settings only - read only
290         cdef = PLCConfiguration(main_config)
291
292         # in effect : default settings + local settings - read only
293         cread = PLCConfiguration(main_config)
294
295     except:
296         print ("default config files not found, is myplc installed ?")
297         return 1
298
299     # local settings only, will be modified & saved
300     cwrite=PLCConfiguration()
301     
302     try:
303         cread.load(site_config)
304         cwrite.load(site_config)
305     except:
306         cwrite = PLCConfiguration()
307
308         print ("This is %s - %s -- Type ? at the prompt for help" %(command,release))
309     mainloop (cdef, cread, cwrite,main_config, site_config, consolidated_config)
310     return 0
311
312 if __name__ == '__main__':
313     main()