3 # Interactively prompts for variable values
4 # expected arguments are
5 # command -d [default-xml [custom-xml [ consolidated-xml ]]]
7 # we use 3 instances of PLCConfiguration throughout:
8 # cdef : models the defaults, from plc_default.xml
9 # cread : merged from plc_default & configs/site.xml
10 # cwrite : site.xml + pending changes
17 from optparse import OptionParser
19 from plc_config import PLCConfiguration
20 from plc_config import ConfigurationException
24 release_rev = "$Revision$"
29 def noop_validator(v):
32 def plc_validator(validated_variables):
33 maint_user = validated_variables["PLC_API_MAINTENANCE_USER"]
34 root_user = validated_variables["PLC_ROOT_USER"]
35 if maint_user == root_user:
36 raise ConfigurationException("The Maintenance Account email address cannot be the same as the Root User email address")
38 flavours["plc"]={'service':"plc",
39 'usual_variables':["PLC_NAME",
45 "PLC_MAIL_SUPPORT_ADDRESS",
53 'config_dir':"/etc/planetlab",
54 'validate_variables':{"PLC_API":"MAINTENANCE_USER","PLC":"ROOT_USER"},
55 'validator':plc_validator,
58 defined_flavour = "plc"
60 # historically we could also configure the devel pkg....
61 def init_flavour (flavour):
62 global service, usual_variables
64 global defined_flavour
65 if flavours.has_key(flavour):
66 defined_flavour = flavour
68 defined_flavour = "plc"
70 flav=flavours.get(flavour,flavours["plc"])
71 service=flav["service"]
72 usual_variables=flav["usual_variables"]
73 config_dir=flav["config_dir"]
75 global def_default_config, def_site_config, def_consolidated_config
76 def_default_config= "%s/default_config.xml" % config_dir
77 def_site_config = "%s/configs/site.xml" % config_dir
78 def_consolidated_config = "%s/plc_config.xml" % config_dir
81 mainloop_usage= """Available commands:
82 Uppercase versions give variables comments, when available
83 u/U\t\t\tEdit usual variables
84 w/W\t\t\tWrite / Write & reload
85 r\t\t\tRestart %s service
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>]List Variables (all, in category, single)
96 Typical usage involves: u, [l,] w, r, q
100 command_usage="%prog [options] [default-xml [site-xml [consolidated-xml]]]"
103 \t default-xml defaults to %s
104 \t site-xml defaults to %s
105 \t consolidated-xml defaults to %s""" % (def_default_config,def_site_config, def_consolidated_config)
107 \t default-xml defaults to %s
108 \t site-xml defaults to %s
109 \t consolidated-xml defaults to %s""" % (def_default_config,def_site_config, def_consolidated_config)
113 variable_usage= """Edit Commands :
114 #\tShow variable comments
115 .\tStops prompting, return to mainloop
116 /\tCleans any site-defined value, reverts to default
117 =\tShows default value
118 >\tSkips to next category
123 def get_value (config, category_id, variable_id):
124 (category, variable) = config.get (category_id, variable_id)
125 return variable['value']
127 def get_current_value (cread, cwrite, category_id, variable_id):
128 # the value stored in cwrite, if present, is the one we want
130 result=get_value (cwrite,category_id,variable_id)
132 result=get_value (cread,category_id,variable_id)
135 # refrain from using plc_config's _sanitize
136 def get_varname (config, category_id, variable_id):
137 (category, variable) = config.get (category_id, variable_id)
138 return (category_id+"_"+variable['id']).upper()
140 # could not avoid using _sanitize here..
141 def get_name_comments (config, cid, vid):
143 (category, variable) = config.get (cid, vid)
144 (id, name, value, comments) = config._sanitize_variable (cid,variable)
145 return (name,comments)
149 def print_name_comments (config, cid, vid):
150 (name,comments)=get_name_comments(config,cid,vid)
152 print "### %s" % name
154 for line in comments:
157 print "!!! No comment associated to %s_%s" % (cid,vid)
160 def list_categories (config):
162 for (category_id, (category, variables)) in config.variables().iteritems():
163 result += [category_id]
166 def print_categories (config):
167 print "Known categories"
168 for cid in list_categories(config):
169 print "%s" % (cid.upper())
172 def list_category (config, cid):
174 for (category_id, (category, variables)) in config.variables().iteritems():
175 if (cid == category_id):
176 for variable in variables.values():
177 result += ["%s_%s" %(cid,variable['id'])]
180 def print_category (config, cid, show_comments=True):
183 vids=list_category(config,cid)
185 print "%s : no such category"%CID
187 print "Category %s contains" %(CID)
192 def consolidate (default_config, site_config, consolidated_config):
195 conso = PLCConfiguration (default_config)
196 conso.load (site_config)
197 conso.save (consolidated_config)
198 except Exception, inst:
199 print "Could not consolidate, %s" % (str(inst))
201 print ("Merged\n\t%s\nand\t%s\ninto\t%s"%(default_config,site_config,
202 consolidated_config))
204 def reload_service ():
206 os.system("set -x ; service %s reload" % service)
209 def restart_service ():
211 print ("==================== Stopping %s" % service)
212 os.system("service %s stop" % service)
213 print ("==================== Starting %s" % service)
214 os.system("service %s start" % service)
217 def prompt_variable (cdef, cread, cwrite, category, variable,
218 show_comments, support_next=False):
220 assert category.has_key('id')
221 assert variable.has_key('id')
223 category_id = category ['id']
224 variable_id = variable['id']
227 default_value = get_value(cdef,category_id,variable_id)
228 current_value = get_current_value(cread,cwrite,category_id, variable_id)
229 varname = get_varname (cread,category_id, variable_id)
232 print_name_comments (cdef, category_id, variable_id)
233 prompt = "== %s : [%s] " % (varname,current_value)
235 answer = raw_input(prompt).strip()
237 raise Exception ('BailOut')
238 except KeyboardInterrupt:
240 raise Exception ('BailOut')
243 if (answer == "") or (answer == current_value):
245 elif (answer == "."):
246 raise Exception ('BailOut')
247 elif (answer == "#"):
248 print_name_comments(cread,category_id,variable_id)
249 elif (answer == "?"):
250 print variable_usage.strip()
251 elif (answer == "="):
252 print ("%s defaults to %s" %(varname,default_value))
253 # revert to default : remove from cwrite (i.e. site-config)
254 elif (answer == "/"):
255 cwrite.delete(category_id,variable_id)
256 print ("%s reverted to %s" %(varname,default_value))
258 elif (answer == ">"):
260 raise Exception ('NextCategory')
262 print "No support for next category"
264 variable['value'] = answer
265 cwrite.set(category,variable)
268 def prompt_variables_all (cdef, cread, cwrite, show_comments):
270 for (category_id, (category, variables)) in cread.variables().iteritems():
271 print ("========== Category = %s" % category_id.upper())
272 for variable in variables.values():
274 newvar = prompt_variable (cdef, cread, cwrite, category, variable,
276 except Exception, inst:
277 if (str(inst) == 'NextCategory'): break
280 except Exception, inst:
281 if (str(inst) == 'BailOut'): return
284 def prompt_variables_category (cdef, cread, cwrite, cid, show_comments):
288 print ("========== Category = %s" % CID)
289 for vid in list_category(cdef,cid):
290 (category,variable) = cdef.locate_varname(vid.upper())
291 newvar = prompt_variable (cdef, cread, cwrite, category, variable,
292 show_comments, False)
293 except Exception, inst:
294 if (str(inst) == 'BailOut'): return
298 def show_variable (cdef, cread, cwrite,
299 category, variable,show_value,show_comments):
300 assert category.has_key('id')
301 assert variable.has_key('id')
303 category_id = category ['id']
304 variable_id = variable['id']
306 default_value = get_value(cdef,category_id,variable_id)
307 current_value = get_current_value(cread,cwrite,category_id,variable_id)
308 varname = get_varname (cread,category_id, variable_id)
310 print_name_comments (cdef, category_id, variable_id)
312 print "%s = %s" % (varname,current_value)
314 print "%s" % (varname)
316 def show_variables_all (cdef, cread, cwrite, show_value, show_comments):
317 for (category_id, (category, variables)) in cread.variables().iteritems():
318 print ("========== Category = %s" % category_id.upper())
319 for variable in variables.values():
320 show_variable (cdef, cread, cwrite,
321 category, variable,show_value,show_comments)
323 def show_variables_category (cdef, cread, cwrite, cid, show_value,show_comments):
326 print ("========== Category = %s" % CID)
327 for vid in list_category(cdef,cid):
328 (category,variable) = cdef.locate_varname(vid.upper())
329 show_variable (cdef, cread, cwrite, category, variable,
330 show_value,show_comments)
333 re_mainloop_0arg="^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
334 re_mainloop_1arg="^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
335 matcher_mainloop_0arg=re.compile(re_mainloop_0arg)
336 matcher_mainloop_1arg=re.compile(re_mainloop_1arg)
338 def mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config):
342 answer = raw_input("Enter command (u for usual changes, w to save, ? for help) ").strip()
345 except KeyboardInterrupt:
349 if (answer == "") or (answer in "?hH"):
352 groups_parse = matcher_mainloop_0arg.match(answer)
355 command = groups_parse.group('command')
358 groups_parse = matcher_mainloop_1arg.match(answer)
360 command = groups_parse.group('command')
361 arg=groups_parse.group('arg')
363 print ("Unknown command >%s< -- use h for help" % answer)
366 show_comments=command.isupper()
367 command=command.lower()
373 variables=list_category (cdef,arg)
375 # category_id as the category name
376 # variables as the list of variable names
380 (category,variable)=cdef.locate_varname(arg)
382 # category/variable as output by locate_varname
385 print "%s: no such category or variable" % arg
388 if (command in "qQ"):
389 # todo check confirmation
391 elif (command == "w"):
392 global defined_flavour
394 # Confirm that various constraints are met before saving file.
395 validate_variables = flavours[defined_flavour].get('validate_variables',{})
396 validated_variables = cwrite.verify(cdef, cread, validate_variables)
397 validator = flavours[defined_flavour].get('validator',noop_validator)
398 validator(validated_variables)
399 cwrite.save(site_config)
400 except ConfigurationException, e:
401 print "Save failed due to a configuration exception: %s" % e
404 print traceback.print_exc()
405 print ("Could not save -- fix write access on %s" % site_config)
407 print ("Wrote %s" % site_config)
408 consolidate(default_config, site_config, consolidated_config)
409 print ("You might want to type 'r' (restart %s), 'R' (reload %s) or 'q' (quit)" % \
411 elif (command == "u"):
413 for varname in usual_variables:
414 (category,variable) = cdef.locate_varname(varname)
415 prompt_variable(cdef, cread, cwrite, category, variable, False)
416 except Exception, inst:
417 if (str(inst) != 'BailOut'):
419 elif (command == "r"):
421 elif (command == "R"):
423 elif (command == "c"):
424 print_categories(cread)
425 elif (command in "eE"):
427 prompt_variables_all(cdef, cread, cwrite,show_comments)
428 elif mode == 'CATEGORY':
429 prompt_variables_category(cdef,cread,cwrite,category_id,show_comments)
430 elif mode == 'VARIABLE':
432 prompt_variable (cdef,cread,cwrite,category,variable,
434 except Exception, inst:
435 if (str(inst) != 'BailOut'):
437 elif (command in "vVsSlL"):
438 show_value=(command in "sSlL")
439 (c1,c2,c3) = (cdef, cread, cwrite)
440 if (command in "lL"):
441 (c1,c2,c3) = (cwrite,cwrite,cwrite)
443 show_variables_all(c1,c2,c3,show_value,show_comments)
444 elif mode == 'CATEGORY':
445 show_variables_category(c1,c2,c3,category_id,show_value,show_comments)
446 elif mode == 'VARIABLE':
447 show_variable (c1,c2,c3,category,variable,show_value,show_comments)
449 print ("Unknown command >%s< -- use h for help" % answer)
452 # creates directory for file if not yet existing
453 def check_dir (config_file):
454 dirname = os.path.dirname (config_file)
455 if (not os.path.exists (dirname)):
457 os.makedirs(dirname,0755)
459 print "Cannot create dir %s due to %s - exiting" % (dirname,e)
462 if (not os.path.exists (dirname)):
463 print "Cannot create dir %s - exiting" % dirname
466 print "Created directory %s" % dirname
474 parser = OptionParser(usage=usage(), version="%prog 1.0")
475 parser.set_defaults(flavour="plc",
476 config="flavour.config",
480 parser.add_option("","--configdir",dest="config_dir",help="specify configuration directory")
481 parser.add_option("","--service",dest="service",help="specify /etc/init.d style service name")
482 parser.add_option("","--usual_variable",dest="usual_variables",action="append", help="add a usual variable")
483 parser.add_option("","--flavour",dest="flavour", help="Sets the configuration flavour")
485 (config,args) = parser.parse_args()
487 parser.error("too many arguments")
489 if config.flavour not in flavours:
490 if config.service==None:
491 parser.error("unknown flavour '%s'" % config.flavour)
493 flavours[config.flavour]={}
494 flavour=flavours[config.flavour]
495 flavour['service']=config.service
496 flavour['usual_variables']=config.usual_variables
497 if config.config_dir==None:
498 flavour['config_dir']="/etc/%s"%config.service
500 flavour['config_dir']=config.config_dir
502 flavour=flavours[config.flavour]
504 # in case the config dir should be something other than /etc/planetlab
505 if config.config_dir <> None:
506 flavour['config_dir']=config.config_dir
508 # add in new usual_variables defined on the command line
509 for usual_variable in config.usual_variables:
510 if usual_variable not in flavour['usual_variables']:
511 flavour['usual_variables'].append(usual_variable)
514 init_flavour(config.flavour)
516 (default_config,site_config,consolidated_config) = (def_default_config, def_site_config, def_consolidated_config)
518 default_config=args[0]
522 consolidated_config=args[2]
524 for c in (default_config,site_config,consolidated_config):
528 # the default settings only - read only
529 cdef = PLCConfiguration(default_config)
531 # in effect : default settings + local settings - read only
532 cread = PLCConfiguration(default_config)
534 except ConfigurationException, e:
535 print ("Error %s in default config file %s" %(e,default_config))
538 print traceback.print_exc()
539 print ("default config files %s not found, is myplc installed ?" % default_config)
543 # local settings only, will be modified & saved
544 cwrite=PLCConfiguration()
547 cread.load(site_config)
548 cwrite.load(site_config)
550 cwrite = PLCConfiguration()
552 mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config)
555 if __name__ == '__main__':