3 # Interactively prompts for variable values
4 # expected arguments are
5 # command -d [default-xml [custom-xml [ consolidated-xml ]]]
7 # -d is for the myplc-devel package
9 # we use 3 instances of PLCConfiguration throughout:
10 # cdef : models the defaults, from plc_default.xml
11 # cread : merged from plc_default & configs/site.xml
12 # cwrite : site.xml + pending changes
19 from optparse import OptionParser
21 from plc_config import PLCConfiguration
22 from plc_config import ConfigurationException
26 release_rev = "$Revision$"
30 flavours["plc-devel"]={'service':"plc-devel",
31 'usual_variables':["PLC_DEVEL_FEDORA_URL","PLC_DEVEL_CVSROOT"],
32 'config_dir':"/plc/devel/data/etc/planetlab"}
34 def noop_validator(v):
37 def plc_validator(validated_variables):
38 maint_user = validated_variables["PLC_API_MAINTENANCE_USER"]
39 root_user = validated_variables["PLC_ROOT_USER"]
40 if maint_user == root_user:
41 raise ConfigurationException("The Maintenance Account email address cannot be the same as the Root User email address")
43 flavours["plc"]={'service':"plc",
44 'usual_variables':["PLC_NAME",
50 "PLC_MAIL_SUPPORT_ADDRESS",
58 'config_dir':"/etc/planetlab",
59 'validate_variables':{"PLC_API":"MAINTENANCE_USER","PLC":"ROOT_USER"},
60 'validator':plc_validator,
63 defined_flavour = "plc"
66 def init_flavour (flavour):
67 global service, usual_variables
69 global defined_flavour
70 if flavours.has_key(flavour):
71 defined_flavour = flavour
73 defined_flavour = "plc"
75 flav=flavours.get(flavour,flavours["plc"])
76 service=flav["service"]
77 usual_variables=flav["usual_variables"]
78 config_dir=flav["config_dir"]
80 global def_default_config, def_site_config, def_consolidated_config
81 def_default_config= "%s/default_config.xml" % config_dir
82 def_site_config = "%s/configs/site.xml" % config_dir
83 def_consolidated_config = "%s/plc_config.xml" % config_dir
86 mainloop_usage= """Available commands:
87 Uppercase versions give variables comments, when available
88 u/U\t\t\tEdit usual variables
89 w/W\t\t\tWrite / Write & reload
90 r\t\t\tRestart %s service
91 q\t\t\tQuit (without saving)
94 l/L [<cat>|<var>]\tShow Locally modified variables/values
95 s/S [<cat>|<var>]\tShow variables/values (all, in category, single)
96 e/E [<cat>|<var>]\tEdit variables (all, in category, single)
98 c\t\t\tList categories
99 v/V [<cat>|<var>]List Variables (all, in category, single)
101 Typical usage involves: u, [l,] w, r, q
105 command_usage="%prog [options] [default-xml [site-xml [consolidated-xml]]]"
108 \t default-xml defaults to %s
109 \t site-xml defaults to %s
110 \t consolidated-xml defaults to %s""" % (def_default_config,def_site_config, def_consolidated_config)
112 Unless you specify the -d option, meaning you want to configure
113 myplc-devel instead of regular myplc, in which case"""
114 init_flavour ("plc-devel")
116 \t default-xml defaults to %s
117 \t site-xml defaults to %s
118 \t consolidated-xml defaults to %s""" % (def_default_config,def_site_config, def_consolidated_config)
122 variable_usage= """Edit Commands :
123 #\tShow variable comments
124 .\tStops prompting, return to mainloop
125 /\tCleans any site-defined value, reverts to default
126 =\tShows default value
127 >\tSkips to next category
132 def get_value (config, category_id, variable_id):
133 (category, variable) = config.get (category_id, variable_id)
134 return variable['value']
136 def get_current_value (cread, cwrite, category_id, variable_id):
137 # the value stored in cwrite, if present, is the one we want
139 result=get_value (cwrite,category_id,variable_id)
141 result=get_value (cread,category_id,variable_id)
144 # refrain from using plc_config's _sanitize
145 def get_varname (config, category_id, variable_id):
146 (category, variable) = config.get (category_id, variable_id)
147 return (category_id+"_"+variable['id']).upper()
149 # could not avoid using _sanitize here..
150 def get_name_comments (config, cid, vid):
152 (category, variable) = config.get (cid, vid)
153 (id, name, value, comments) = config._sanitize_variable (cid,variable)
154 return (name,comments)
158 def print_name_comments (config, cid, vid):
159 (name,comments)=get_name_comments(config,cid,vid)
161 print "### %s" % name
163 for line in comments:
166 print "!!! No comment associated to %s_%s" % (cid,vid)
169 def list_categories (config):
171 for (category_id, (category, variables)) in config.variables().iteritems():
172 result += [category_id]
175 def print_categories (config):
176 print "Known categories"
177 for cid in list_categories(config):
178 print "%s" % (cid.upper())
181 def list_category (config, cid):
183 for (category_id, (category, variables)) in config.variables().iteritems():
184 if (cid == category_id):
185 for variable in variables.values():
186 result += ["%s_%s" %(cid,variable['id'])]
189 def print_category (config, cid, show_comments=True):
192 vids=list_category(config,cid)
194 print "%s : no such category"%CID
196 print "Category %s contains" %(CID)
201 def consolidate (default_config, site_config, consolidated_config):
204 conso = PLCConfiguration (default_config)
205 conso.load (site_config)
206 conso.save (consolidated_config)
207 except Exception, inst:
208 print "Could not consolidate, %s" % (str(inst))
210 print ("Merged\n\t%s\nand\t%s\ninto\t%s"%(default_config,site_config,
211 consolidated_config))
213 def reload_service ():
215 os.system("set -x ; service %s reload" % service)
218 def restart_service ():
220 print ("==================== Stopping %s" % service)
221 os.system("service %s stop" % service)
222 print ("==================== Starting %s" % service)
223 os.system("service %s start" % service)
226 def prompt_variable (cdef, cread, cwrite, category, variable,
227 show_comments, support_next=False):
229 assert category.has_key('id')
230 assert variable.has_key('id')
232 category_id = category ['id']
233 variable_id = variable['id']
236 default_value = get_value(cdef,category_id,variable_id)
237 current_value = get_current_value(cread,cwrite,category_id, variable_id)
238 varname = get_varname (cread,category_id, variable_id)
241 print_name_comments (cdef, category_id, variable_id)
242 prompt = "== %s : [%s] " % (varname,current_value)
244 answer = raw_input(prompt).strip()
246 raise Exception ('BailOut')
247 except KeyboardInterrupt:
249 raise Exception ('BailOut')
252 if (answer == "") or (answer == current_value):
254 elif (answer == "."):
255 raise Exception ('BailOut')
256 elif (answer == "#"):
257 print_name_comments(cread,category_id,variable_id)
258 elif (answer == "?"):
259 print variable_usage.strip()
260 elif (answer == "="):
261 print ("%s defaults to %s" %(varname,default_value))
262 # revert to default : remove from cwrite (i.e. site-config)
263 elif (answer == "/"):
264 cwrite.delete(category_id,variable_id)
265 print ("%s reverted to %s" %(varname,default_value))
267 elif (answer == ">"):
269 raise Exception ('NextCategory')
271 print "No support for next category"
273 variable['value'] = answer
274 cwrite.set(category,variable)
277 def prompt_variables_all (cdef, cread, cwrite, show_comments):
279 for (category_id, (category, variables)) in cread.variables().iteritems():
280 print ("========== Category = %s" % category_id.upper())
281 for variable in variables.values():
283 newvar = prompt_variable (cdef, cread, cwrite, category, variable,
285 except Exception, inst:
286 if (str(inst) == 'NextCategory'): break
289 except Exception, inst:
290 if (str(inst) == 'BailOut'): return
293 def prompt_variables_category (cdef, cread, cwrite, cid, show_comments):
297 print ("========== Category = %s" % CID)
298 for vid in list_category(cdef,cid):
299 (category,variable) = cdef.locate_varname(vid.upper())
300 newvar = prompt_variable (cdef, cread, cwrite, category, variable,
301 show_comments, False)
302 except Exception, inst:
303 if (str(inst) == 'BailOut'): return
307 def show_variable (cdef, cread, cwrite,
308 category, variable,show_value,show_comments):
309 assert category.has_key('id')
310 assert variable.has_key('id')
312 category_id = category ['id']
313 variable_id = variable['id']
315 default_value = get_value(cdef,category_id,variable_id)
316 current_value = get_current_value(cread,cwrite,category_id,variable_id)
317 varname = get_varname (cread,category_id, variable_id)
319 print_name_comments (cdef, category_id, variable_id)
321 print "%s = %s" % (varname,current_value)
323 print "%s" % (varname)
325 def show_variables_all (cdef, cread, cwrite, show_value, show_comments):
326 for (category_id, (category, variables)) in cread.variables().iteritems():
327 print ("========== Category = %s" % category_id.upper())
328 for variable in variables.values():
329 show_variable (cdef, cread, cwrite,
330 category, variable,show_value,show_comments)
332 def show_variables_category (cdef, cread, cwrite, cid, show_value,show_comments):
335 print ("========== Category = %s" % CID)
336 for vid in list_category(cdef,cid):
337 (category,variable) = cdef.locate_varname(vid.upper())
338 show_variable (cdef, cread, cwrite, category, variable,
339 show_value,show_comments)
342 re_mainloop_0arg="^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
343 re_mainloop_1arg="^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
344 matcher_mainloop_0arg=re.compile(re_mainloop_0arg)
345 matcher_mainloop_1arg=re.compile(re_mainloop_1arg)
347 def mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config):
351 answer = raw_input("Enter command (u for usual changes, w to save, ? for help) ").strip()
354 except KeyboardInterrupt:
358 if (answer == "") or (answer in "?hH"):
361 groups_parse = matcher_mainloop_0arg.match(answer)
364 command = groups_parse.group('command')
367 groups_parse = matcher_mainloop_1arg.match(answer)
369 command = groups_parse.group('command')
370 arg=groups_parse.group('arg')
372 print ("Unknown command >%s< -- use h for help" % answer)
375 show_comments=command.isupper()
376 command=command.lower()
382 variables=list_category (cdef,arg)
384 # category_id as the category name
385 # variables as the list of variable names
389 (category,variable)=cdef.locate_varname(arg)
391 # category/variable as output by locate_varname
394 print "%s: no such category or variable" % arg
397 if (command in "qQ"):
398 # todo check confirmation
400 elif (command == "w"):
401 global defined_flavour
403 # Confirm that various constraints are met before saving file.
404 validate_variables = flavours[defined_flavour].get('validate_variables',{})
405 validated_variables = cwrite.verify(cdef, cread, validate_variables)
406 validator = flavours[defined_flavour].get('validator',noop_validator)
407 validator(validated_variables)
408 cwrite.save(site_config)
409 except ConfigurationException, e:
410 print "Save failed due to a configuration exception: %s" % e
413 print traceback.print_exc()
414 print ("Could not save -- fix write access on %s" % site_config)
416 print ("Wrote %s" % site_config)
417 consolidate(default_config, site_config, consolidated_config)
418 print ("You might want to type 'r' (restart %s), 'R' (reload %s) or 'q' (quit)" % \
420 elif (command == "u"):
422 for varname in usual_variables:
423 (category,variable) = cdef.locate_varname(varname)
424 prompt_variable(cdef, cread, cwrite, category, variable, False)
425 except Exception, inst:
426 if (str(inst) != 'BailOut'):
428 elif (command == "r"):
430 elif (command == "R"):
432 elif (command == "c"):
433 print_categories(cread)
434 elif (command in "eE"):
436 prompt_variables_all(cdef, cread, cwrite,show_comments)
437 elif mode == 'CATEGORY':
438 prompt_variables_category(cdef,cread,cwrite,category_id,show_comments)
439 elif mode == 'VARIABLE':
441 prompt_variable (cdef,cread,cwrite,category,variable,
443 except Exception, inst:
444 if (str(inst) != 'BailOut'):
446 elif (command in "vVsSlL"):
447 show_value=(command in "sSlL")
448 (c1,c2,c3) = (cdef, cread, cwrite)
449 if (command in "lL"):
450 (c1,c2,c3) = (cwrite,cwrite,cwrite)
452 show_variables_all(c1,c2,c3,show_value,show_comments)
453 elif mode == 'CATEGORY':
454 show_variables_category(c1,c2,c3,category_id,show_value,show_comments)
455 elif mode == 'VARIABLE':
456 show_variable (c1,c2,c3,category,variable,show_value,show_comments)
458 print ("Unknown command >%s< -- use h for help" % answer)
461 # creates directory for file if not yet existing
462 def check_dir (config_file):
463 dirname = os.path.dirname (config_file)
464 if (not os.path.exists (dirname)):
466 os.makedirs(dirname,0755)
468 print "Cannot create dir %s due to %s - exiting" % (dirname,e)
471 if (not os.path.exists (dirname)):
472 print "Cannot create dir %s - exiting" % dirname
475 print "Created directory %s" % dirname
483 parser = OptionParser(usage=usage(), version="%prog 1.0")
484 parser.set_defaults(flavour="plc",
486 config="flavour.config",
490 parser.add_option("-d","",dest="devel",action="store_true",help="Sets the configuration flavour")
491 parser.add_option("","--configdir",dest="config_dir",help="specify configuration directory")
492 parser.add_option("","--service",dest="service",help="specify /etc/init.d style service name")
493 parser.add_option("","--usual_variable",dest="usual_variables",action="append", help="add a usual variable")
494 parser.add_option("","--flavour",dest="flavour", help="Sets the configuration flavour")
496 (config,args) = parser.parse_args()
498 parser.error("too many arguments")
500 # if -d then set flavour to "plc-devel"
502 config.flavour="plc-devel"
504 if config.flavour not in flavours:
505 if config.service==None:
506 parser.error("unknown flavour '%s'" % config.flavour)
508 flavours[config.flavour]={}
509 flavour=flavours[config.flavour]
510 flavour['service']=config.service
511 flavour['usual_variables']=config.usual_variables
512 if config.config_dir==None:
513 flavour['config_dir']="/etc/%s"%config.service
515 flavour['config_dir']=config.config_dir
517 flavour=flavours[config.flavour]
519 # in case the config dir should be something other than /etc/planetlab
520 if config.config_dir <> None:
521 flavour['config_dir']=config.config_dir
523 # add in new usual_variables defined on the command line
524 for usual_variable in config.usual_variables:
525 if usual_variable not in flavour['usual_variables']:
526 flavour['usual_variables'].append(usual_variable)
529 init_flavour(config.flavour)
531 (default_config,site_config,consolidated_config) = (def_default_config, def_site_config, def_consolidated_config)
533 default_config=args[0]
537 consolidated_config=args[2]
539 for c in (default_config,site_config,consolidated_config):
543 # the default settings only - read only
544 cdef = PLCConfiguration(default_config)
546 # in effect : default settings + local settings - read only
547 cread = PLCConfiguration(default_config)
549 except ConfigurationException, e:
550 print ("Error %s in default config file %s" %(e,default_config))
553 print traceback.print_exc()
554 print ("default config files %s not found, is myplc installed ?" % default_config)
558 # local settings only, will be modified & saved
559 cwrite=PLCConfiguration()
562 cread.load(site_config)
563 cwrite.load(site_config)
565 cwrite = PLCConfiguration()
567 mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config)
570 if __name__ == '__main__':