3 # Merge PlanetLab Central (PLC) configuration files into a variety of
4 # output formats. These files represent the global configuration for a
7 # Mark Huang <mlhuang@cs.princeton.edu>
8 # Copyright (C) 2006 The Trustees of Princeton University
22 import xml.dom.minidom
23 from xml.parsers.expat import ExpatError
24 from StringIO import StringIO
25 from optparse import OptionParser
27 class ConfigurationException(Exception): pass
29 class PLCConfiguration:
31 Configuration file store. Optionally instantiate with a file path
34 plc = PLCConfiguration()
35 plc = PLCConfiguration(fileobj)
36 plc = PLCConfiguration("/etc/planetlab/plc_config.xml")
38 You may load() additional files later, which will be merged into
39 the current configuration:
41 plc.load("/etc/planetlab/local.xml")
43 You may also save() the configuration. If a file path or object is
44 not specified, the configuration will be written to the file path
45 or object that was first loaded.
48 plc.save("/etc/planetlab/plc_config.xml")
51 def __init__(self, file = None):
52 impl = xml.dom.minidom.getDOMImplementation()
53 self._dom = impl.createDocument(None, "configuration", None)
62 def _get_text(self, node):
64 Get the text of a text node.
67 if node.firstChild and \
68 node.firstChild.nodeType == node.TEXT_NODE:
69 if node.firstChild.data is None:
70 # Interpret simple presence of node as "", not NULL
73 return node.firstChild.data
78 def _get_text_of_child(self, parent, name):
80 Get the text of a (direct) child text node.
83 for node in parent.childNodes:
84 if node.nodeType == node.ELEMENT_NODE and \
86 return self._get_text(node)
91 def _set_text(self, node, data):
93 Set the text of a text node.
96 if node.firstChild and \
97 node.firstChild.nodeType == node.TEXT_NODE:
99 node.removeChild(node.firstChild)
101 node.firstChild.data = data
102 elif data is not None:
105 node.appendChild(text)
108 def _set_text_of_child(self, parent, name, data):
110 Set the text of a (direct) child text node.
113 for node in parent.childNodes:
114 if node.nodeType == node.ELEMENT_NODE and \
115 node.tagName == name:
116 self._set_text(node, data)
119 child = TrimTextElement(name)
120 self._set_text(child, data)
121 parent.appendChild(child)
124 def _category_element_to_dict(self, category_element):
126 Turn a <category> element into a dictionary of its attributes
127 and child text nodes.
131 category['id'] = category_element.getAttribute('id').lower()
132 for node in category_element.childNodes:
133 if node.nodeType == node.ELEMENT_NODE and \
134 node.tagName in ['name', 'description']:
135 category[node.tagName] = self._get_text_of_child(category_element, node.tagName)
136 category['element'] = category_element
141 def _variable_element_to_dict(self, variable_element):
143 Turn a <variable> element into a dictionary of its attributes
144 and child text nodes.
148 variable['id'] = variable_element.getAttribute('id').lower()
149 if variable_element.hasAttribute('type'):
150 variable['type'] = variable_element.getAttribute('type')
151 for node in variable_element.childNodes:
152 if node.nodeType == node.ELEMENT_NODE and \
153 node.tagName in ['name', 'value', 'description']:
154 variable[node.tagName] = self._get_text_of_child(variable_element, node.tagName)
155 variable['element'] = variable_element
160 def _group_element_to_dict(self, group_element):
162 Turn a <group> element into a dictionary of its attributes
163 and child text nodes.
167 for node in group_element.childNodes:
168 if node.nodeType == node.ELEMENT_NODE and \
169 node.tagName in ['id', 'name', 'default', 'description', 'uservisible']:
170 group[node.tagName] = self._get_text_of_child(group_element, node.tagName)
171 group['element'] = group_element
176 def _packagereq_element_to_dict(self, packagereq_element):
178 Turns a <packagereq> element into a dictionary of its attributes
179 and child text nodes.
183 if packagereq_element.hasAttribute('type'):
184 package['type'] = packagereq_element.getAttribute('type')
185 package['name'] = self._get_text(packagereq_element)
186 package['element'] = packagereq_element
191 def load(self, file = "/etc/planetlab/plc_config.xml"):
193 Merge file into configuration store.
197 dom = xml.dom.minidom.parse(file)
198 except ExpatError, e:
199 raise ConfigurationException, e
201 if type(file) in types.StringTypes:
202 self._files.append(os.path.abspath(file))
204 # Parse <variables> section
205 for variables_element in dom.getElementsByTagName('variables'):
206 for category_element in variables_element.getElementsByTagName('category'):
207 category = self._category_element_to_dict(category_element)
208 self.set(category, None)
210 for variablelist_element in category_element.getElementsByTagName('variablelist'):
211 for variable_element in variablelist_element.getElementsByTagName('variable'):
212 variable = self._variable_element_to_dict(variable_element)
213 self.set(category, variable)
215 # Parse <comps> section
216 for comps_element in dom.getElementsByTagName('comps'):
217 for group_element in comps_element.getElementsByTagName('group'):
218 group = self._group_element_to_dict(group_element)
219 self.add_package(group, None)
221 for packagereq_element in group_element.getElementsByTagName('packagereq'):
222 package = self._packagereq_element_to_dict(packagereq_element)
223 self.add_package(group, package)
226 def save(self, file = None):
228 Write configuration store to file.
233 file = self._files[0]
235 file = "/etc/planetlab/plc_config.xml"
237 if type(file) in types.StringTypes:
238 fileobj = open(file, 'w')
243 fileobj.write(self.output_xml())
248 def verify(self, default, read, verify_variables={}):
249 """ Confirm that the existing configuration is consistent
250 according to the checks below.
252 It looks for filled-in values in the order of, local object (self),
253 followed by cread (read values), and finally default values.
257 default configuration
259 list of category/variable tuples to validate in these configurations
263 dict of values for the category/variables passed in
264 If an exception is found, ConfigurationException is raised.
268 validated_variables = {}
269 for category_id, variable_id in verify_variables.iteritems():
270 category_id = category_id.lower()
271 variable_id = variable_id.lower()
272 variable_value = None
273 sources = (self, read, default)
274 for source in sources:
275 (category_value, variable_value) = source.get(category_id,variable_id)
276 if variable_value <> None:
277 entry = validated_variables.get(category_id,[])
278 entry.append(variable_value['value'])
279 validated_variables["%s_%s"%(category_id.upper(),variable_id.upper())]=entry
281 if variable_value == None:
282 raise ConfigurationException("Cannot find %s_%s)" % \
283 (category_id.upper(),
284 variable_id.upper()))
285 return validated_variables
287 def get(self, category_id, variable_id):
289 Get the specified variable in the specified category.
293 category_id = unique category identifier (e.g., 'plc_www')
294 variable_id = unique variable identifier (e.g., 'port')
298 variable = { 'id': "variable_identifier",
299 'type': "variable_type",
300 'value': "variable_value",
301 'name': "Variable name",
302 'description': "Variable description" }
305 if self._variables.has_key(category_id.lower()):
306 (category, variables) = self._variables[category_id]
307 if variables.has_key(variable_id.lower()):
308 variable = variables[variable_id]
315 return (category, variable)
318 def delete(self, category_id, variable_id):
320 Delete the specified variable from the specified category. If
321 variable_id is None, deletes all variables from the specified
322 category as well as the category itself.
326 category_id = unique category identifier (e.g., 'plc_www')
327 variable_id = unique variable identifier (e.g., 'port')
330 if self._variables.has_key(category_id.lower()):
331 (category, variables) = self._variables[category_id]
332 if variable_id is None:
333 category['element'].parentNode.removeChild(category['element'])
334 del self._variables[category_id]
335 elif variables.has_key(variable_id.lower()):
336 variable = variables[variable_id]
337 variable['element'].parentNode.removeChild(variable['element'])
338 del variables[variable_id]
341 def set(self, category, variable):
343 Add and/or update the specified variable. The 'id' fields are
344 mandatory. If a field is not specified and the category and/or
345 variable already exists, the field will not be updated. If
346 'variable' is None, only adds and/or updates the specified
351 category = { 'id': "category_identifier",
352 'name': "Category name",
353 'description': "Category description" }
355 variable = { 'id': "variable_identifier",
356 'type': "variable_type",
357 'value': "variable_value",
358 'name': "Variable name",
359 'description': "Variable description" }
362 if not category.has_key('id') or type(category['id']) not in types.StringTypes:
365 category_id = category['id'].lower()
367 if self._variables.has_key(category_id):
369 (old_category, variables) = self._variables[category_id]
371 # Merge category attributes
372 for tag in ['name', 'description']:
373 if category.has_key(tag):
374 old_category[tag] = category[tag]
375 self._set_text_of_child(old_category['element'], tag, category[tag])
377 category_element = old_category['element']
380 category_element = self._dom.createElement('category')
381 category_element.setAttribute('id', category_id)
382 for tag in ['name', 'description']:
383 if category.has_key(tag):
384 self._set_text_of_child(category_element, tag, category[tag])
386 if self._dom.documentElement.getElementsByTagName('variables'):
387 variables_element = self._dom.documentElement.getElementsByTagName('variables')[0]
389 variables_element = self._dom.createElement('variables')
390 self._dom.documentElement.appendChild(variables_element)
391 variables_element.appendChild(category_element)
394 category['element'] = category_element
396 self._variables[category_id] = (category, variables)
398 if variable is None or not variable.has_key('id') or type(variable['id']) not in types.StringTypes:
401 variable_id = variable['id'].lower()
403 if variables.has_key(variable_id):
405 old_variable = variables[variable_id]
407 # Merge variable attributes
408 for attribute in ['type']:
409 if variable.has_key(attribute):
410 old_variable[attribute] = variable[attribute]
411 old_variable['element'].setAttribute(attribute, variable[attribute])
412 for tag in ['name', 'value', 'description']:
413 if variable.has_key(tag):
414 old_variable[tag] = variable[tag]
415 self._set_text_of_child(old_variable['element'], tag, variable[tag])
418 variable_element = self._dom.createElement('variable')
419 variable_element.setAttribute('id', variable_id)
420 for attribute in ['type']:
421 if variable.has_key(attribute):
422 variable_element.setAttribute(attribute, variable[attribute])
423 for tag in ['name', 'value', 'description']:
424 if variable.has_key(tag):
425 self._set_text_of_child(variable_element, tag, variable[tag])
427 if category_element.getElementsByTagName('variablelist'):
428 variablelist_element = category_element.getElementsByTagName('variablelist')[0]
430 variablelist_element = self._dom.createElement('variablelist')
431 category_element.appendChild(variablelist_element)
432 variablelist_element.appendChild(variable_element)
435 variable['element'] = variable_element
436 variables[variable_id] = variable
439 def locate_varname (self, varname):
441 Locates category and variable from a variable's (shell) name
444 (variable, category) when found
445 (None, None) otherwise
448 for (category_id, (category, variables)) in self._variables.iteritems():
449 for variable in variables.values():
450 (id, name, value, comments) = self._sanitize_variable(category_id, variable)
452 return (category,variable)
455 def get_package(self, group_id, package_name):
457 Get the specified package in the specified package group.
461 group_id - unique group id (e.g., 'plc')
462 package_name - unique package name (e.g., 'postgresql')
466 package = { 'name': "package_name",
467 'type': "mandatory|optional" }
470 if self._packages.has_key(group_id.lower()):
471 (group, packages) = self._packages[group_id]
472 if packages.has_key(package_name):
473 package = packages[package_name]
480 return (group, package)
483 def delete_package(self, group_id, package_name):
485 Deletes the specified variable from the specified category. If
486 variable_id is None, deletes all variables from the specified
487 category as well as the category itself.
491 group_id - unique group id (e.g., 'plc')
492 package_name - unique package name (e.g., 'postgresql')
495 if self._packages.has_key(group_id):
496 (group, packages) = self._packages[group_id]
497 if package_name is None:
498 group['element'].parentNode.removeChild(group['element'])
499 del self._packages[group_id]
500 elif packages.has_key(package_name.lower()):
501 package = packages[package_name]
502 package['element'].parentNode.removeChild(package['element'])
503 del packages[package_name]
506 def add_package(self, group, package):
508 Add and/or update the specified package. The 'id' and 'name'
509 fields are mandatory. If a field is not specified and the
510 package or group already exists, the field will not be
511 updated. If package is None, only adds/or updates the
516 group = { 'id': "group_identifier",
517 'name': "Group name",
518 'default': "true|false",
519 'description': "Group description",
520 'uservisible': "true|false" }
522 package = { 'name': "package_name",
523 'type': "mandatory|optional" }
526 if not group.has_key('id'):
529 group_id = group['id']
531 if self._packages.has_key(group_id):
533 (old_group, packages) = self._packages[group_id]
535 # Merge group attributes
536 for tag in ['id', 'name', 'default', 'description', 'uservisible']:
537 if group.has_key(tag):
538 old_group[tag] = group[tag]
539 self._set_text_of_child(old_group['element'], tag, group[tag])
541 group_element = old_group['element']
544 group_element = self._dom.createElement('group')
545 for tag in ['id', 'name', 'default', 'description', 'uservisible']:
546 if group.has_key(tag):
547 self._set_text_of_child(group_element, tag, group[tag])
549 if self._dom.documentElement.getElementsByTagName('comps'):
550 comps_element = self._dom.documentElement.getElementsByTagName('comps')[0]
552 comps_element = self._dom.createElement('comps')
553 self._dom.documentElement.appendChild(comps_element)
554 comps_element.appendChild(group_element)
557 group['element'] = group_element
559 self._packages[group_id] = (group, packages)
561 if package is None or not package.has_key('name'):
564 package_name = package['name']
565 if packages.has_key(package_name):
567 old_package = packages[package_name]
569 # Merge variable attributes
570 for attribute in ['type']:
571 if package.has_key(attribute):
572 old_package[attribute] = package[attribute]
573 old_package['element'].setAttribute(attribute, package[attribute])
576 packagereq_element = TrimTextElement('packagereq')
577 self._set_text(packagereq_element, package_name)
578 for attribute in ['type']:
579 if package.has_key(attribute):
580 packagereq_element.setAttribute(attribute, package[attribute])
582 if group_element.getElementsByTagName('packagelist'):
583 packagelist_element = group_element.getElementsByTagName('packagelist')[0]
585 packagelist_element = self._dom.createElement('packagelist')
586 group_element.appendChild(packagelist_element)
587 packagelist_element.appendChild(packagereq_element)
590 package['element'] = packagereq_element
591 packages[package_name] = package
596 Return all variables.
600 variables = { 'category_id': (category, variablelist) }
602 category = { 'id': "category_identifier",
603 'name': "Category name",
604 'description': "Category description" }
606 variablelist = { 'variable_id': variable }
608 variable = { 'id': "variable_identifier",
609 'type': "variable_type",
610 'value': "variable_value",
611 'name': "Variable name",
612 'description': "Variable description" }
615 return self._variables
624 packages = { 'group_id': (group, packagelist) }
626 group = { 'id': "group_identifier",
627 'name': "Group name",
628 'default': "true|false",
629 'description': "Group description",
630 'uservisible': "true|false" }
632 packagelist = { 'package_name': package }
634 package = { 'name': "package_name",
635 'type': "mandatory|optional" }
638 return self._packages
641 def _sanitize_variable(self, category_id, variable):
642 assert variable.has_key('id')
643 # Prepend variable name with category label
644 id = category_id + "_" + variable['id']
648 if variable.has_key('type'):
649 type = variable['type']
653 if variable.has_key('name'):
654 name = variable['name']
658 if variable.has_key('value') and variable['value'] is not None:
659 value = variable['value']
660 if type == "int" or type == "double":
661 # bash, Python, and PHP do not require that numbers be quoted
663 elif type == "boolean":
664 # bash, Python, and PHP can all agree on 0 and 1
670 # bash, Python, and PHP all support strong single quoting
671 value = "'" + value.replace("'", "\\'") + "'"
675 if variable.has_key('description') and variable['description'] is not None:
676 description = variable['description']
677 # Collapse consecutive whitespace
678 description = re.sub(r'\s+', ' ', description)
679 # Wrap comments at 70 columns
680 wrapper = textwrap.TextWrapper()
681 comments = wrapper.wrap(description)
685 return (id, name, value, comments)
690 DO NOT EDIT. This file was automatically generated at
694 """ % (time.asctime(), os.linesep.join(self._files))
696 # Get rid of the surrounding newlines
697 return header.strip().split(os.linesep)
700 def output_shell(self, show_comments = True, encoding = "utf-8"):
702 Return variables as a shell script.
705 buf = codecs.lookup(encoding)[3](StringIO())
706 buf.writelines(["# " + line + os.linesep for line in self._header()])
708 for (category_id, (category, variables)) in self._variables.iteritems():
709 for variable in variables.values():
710 (id, name, value, comments) = self._sanitize_variable(category_id, variable)
712 buf.write(os.linesep)
714 buf.write("# " + name + os.linesep)
715 if comments is not None:
716 buf.writelines(["# " + line + os.linesep for line in comments])
717 # bash does not have the concept of NULL
718 if value is not None:
719 buf.write(id + "=" + value + os.linesep)
721 return buf.getvalue()
724 def output_php(self, encoding = "utf-8"):
726 Return variables as a PHP script.
729 buf = codecs.lookup(encoding)[3](StringIO())
730 buf.write("<?php" + os.linesep)
731 buf.writelines(["// " + line + os.linesep for line in self._header()])
733 for (category_id, (category, variables)) in self._variables.iteritems():
734 for variable in variables.values():
735 (id, name, value, comments) = self._sanitize_variable(category_id, variable)
736 buf.write(os.linesep)
738 buf.write("// " + name + os.linesep)
739 if comments is not None:
740 buf.writelines(["// " + line + os.linesep for line in comments])
743 buf.write("define('%s', %s);" % (id, value) + os.linesep)
745 buf.write("?>" + os.linesep)
747 return buf.getvalue()
750 def output_xml(self, encoding = "utf-8"):
752 Return variables in original XML format.
755 buf = codecs.lookup(encoding)[3](StringIO())
756 self._dom.writexml(buf, addindent = " ", indent = "", newl = "\n", encoding = encoding)
758 return buf.getvalue()
761 def output_variables(self, encoding = "utf-8"):
763 Return list of all variable names.
766 buf = codecs.lookup(encoding)[3](StringIO())
768 for (category_id, (category, variables)) in self._variables.iteritems():
769 for variable in variables.values():
770 (id, name, value, comments) = self._sanitize_variable(category_id, variable)
771 buf.write(id + os.linesep)
773 return buf.getvalue()
776 def output_packages(self, encoding = "utf-8"):
778 Return list of all packages.
781 buf = codecs.lookup(encoding)[3](StringIO())
783 for (group, packages) in self._packages.values():
784 buf.write(os.linesep.join(packages.keys()))
787 buf.write(os.linesep)
789 return buf.getvalue()
792 def output_groups(self, encoding = "utf-8"):
794 Return list of all package group names.
797 buf = codecs.lookup(encoding)[3](StringIO())
799 for (group, packages) in self._packages.values():
800 buf.write(group['name'] + os.linesep)
802 return buf.getvalue()
805 def output_comps(self, encoding = "utf-8"):
807 Return <comps> section of configuration.
810 if self._dom is None or \
811 not self._dom.getElementsByTagName("comps"):
813 comps = self._dom.getElementsByTagName("comps")[0]
815 impl = xml.dom.minidom.getDOMImplementation()
816 doc = impl.createDocument(None, "comps", None)
818 buf = codecs.lookup(encoding)[3](StringIO())
820 # Pop it off the DOM temporarily
821 parent = comps.parentNode
822 parent.removeChild(comps)
824 doc.replaceChild(comps, doc.documentElement)
825 doc.writexml(buf, encoding = encoding)
828 parent.appendChild(comps)
830 return buf.getvalue()
833 # xml.dom.minidom.Text.writexml adds surrounding whitespace to textual
834 # data when pretty-printing. Override this behavior.
835 class TrimText(xml.dom.minidom.Text):
836 def writexml(self, writer, indent="", addindent="", newl=""):
837 xml.dom.minidom.Text.writexml(self, writer, "", "", "")
840 class TrimTextElement(xml.dom.minidom.Element):
841 def writexml(self, writer, indent="", addindent="", newl=""):
843 xml.dom.minidom.Element.writexml(self, writer, "", "", "")
851 release_rev = "$Revision:$"
852 release_url = "$URL:$"
859 def noop_validator(validated_variables):
863 # historically we could also configure the devel pkg....
864 def init_configuration ():
865 global g_configuration
866 global usual_variables, config_dir, service
868 usual_variables=g_configuration["usual_variables"]
869 config_dir=g_configuration["config_dir"]
870 service=g_configuration["service"]
872 global def_default_config, def_site_config, def_consolidated_config
873 def_default_config= "%s/default_config.xml" % config_dir
874 def_site_config = "%s/configs/site.xml" % config_dir
875 def_consolidated_config = "%s/%s_config.xml" % (config_dir, service)
877 global mainloop_usage
878 mainloop_usage= """Available commands:
879 Uppercase versions give variables comments, when available
880 u/U\t\t\tEdit usual variables
881 w/W\t\t\tWrite / Write & reload
882 r\t\t\tRestart %s service
883 q\t\t\tQuit (without saving)
886 l/L [<cat>|<var>]\tShow Locally modified variables/values
887 s/S [<cat>|<var>]\tShow variables/values (all, in category, single)
888 e/E [<cat>|<var>]\tEdit variables (all, in category, single)
890 c\t\t\tList categories
891 v/V [<cat>|<var>]List Variables (all, in category, single)
893 Typical usage involves: u, [l,] w, r, q
897 command_usage="%prog [options] [default-xml [site-xml [consolidated-xml]]]"
898 init_configuration ()
900 \t default-xml defaults to %s
901 \t site-xml defaults to %s
902 \t consolidated-xml defaults to %s""" % (def_default_config,def_site_config, def_consolidated_config)
906 variable_usage= """Edit Commands :
907 #\tShow variable comments
908 .\tStops prompting, return to mainloop
909 /\tCleans any site-defined value, reverts to default
910 =\tShows default value
911 >\tSkips to next category
916 def get_value (config, category_id, variable_id):
917 (category, variable) = config.get (category_id, variable_id)
918 return variable['value']
920 def get_current_value (cread, cwrite, category_id, variable_id):
921 # the value stored in cwrite, if present, is the one we want
923 result=get_value (cwrite,category_id,variable_id)
925 result=get_value (cread,category_id,variable_id)
928 # refrain from using plc_config's _sanitize
929 def get_varname (config, category_id, variable_id):
930 (category, variable) = config.get (category_id, variable_id)
931 return (category_id+"_"+variable['id']).upper()
933 # could not avoid using _sanitize here..
934 def get_name_comments (config, cid, vid):
936 (category, variable) = config.get (cid, vid)
937 (id, name, value, comments) = config._sanitize_variable (cid,variable)
938 return (name,comments)
942 def print_name_comments (config, cid, vid):
943 (name,comments)=get_name_comments(config,cid,vid)
945 print "### %s" % name
947 for line in comments:
950 print "!!! No comment associated to %s_%s" % (cid,vid)
953 def list_categories (config):
955 for (category_id, (category, variables)) in config.variables().iteritems():
956 result += [category_id]
959 def print_categories (config):
960 print "Known categories"
961 for cid in list_categories(config):
962 print "%s" % (cid.upper())
965 def list_category (config, cid):
967 for (category_id, (category, variables)) in config.variables().iteritems():
968 if (cid == category_id):
969 for variable in variables.values():
970 result += ["%s_%s" %(cid,variable['id'])]
973 def print_category (config, cid, show_comments=True):
976 vids=list_category(config,cid)
978 print "%s : no such category"%CID
980 print "Category %s contains" %(CID)
985 def consolidate (default_config, site_config, consolidated_config):
988 conso = PLCConfiguration (default_config)
989 conso.load (site_config)
990 conso.save (consolidated_config)
991 except Exception, inst:
992 print "Could not consolidate, %s" % (str(inst))
994 print ("Merged\n\t%s\nand\t%s\ninto\t%s"%(default_config,site_config,
995 consolidated_config))
997 def reload_service ():
999 os.system("set -x ; service %s reload" % service)
1001 ####################
1002 def restart_service ():
1004 print ("==================== Stopping %s" % service)
1005 os.system("service %s stop" % service)
1006 print ("==================== Starting %s" % service)
1007 os.system("service %s start" % service)
1009 ####################
1010 def prompt_variable (cdef, cread, cwrite, category, variable,
1011 show_comments, support_next=False):
1013 assert category.has_key('id')
1014 assert variable.has_key('id')
1016 category_id = category ['id']
1017 variable_id = variable['id']
1020 default_value = get_value(cdef,category_id,variable_id)
1021 current_value = get_current_value(cread,cwrite,category_id, variable_id)
1022 varname = get_varname (cread,category_id, variable_id)
1025 print_name_comments (cdef, category_id, variable_id)
1026 prompt = "== %s : [%s] " % (varname,current_value)
1028 answer = raw_input(prompt).strip()
1030 raise Exception ('BailOut')
1031 except KeyboardInterrupt:
1033 raise Exception ('BailOut')
1036 if (answer == "") or (answer == current_value):
1038 elif (answer == "."):
1039 raise Exception ('BailOut')
1040 elif (answer == "#"):
1041 print_name_comments(cread,category_id,variable_id)
1042 elif (answer == "?"):
1043 print variable_usage.strip()
1044 elif (answer == "="):
1045 print ("%s defaults to %s" %(varname,default_value))
1046 # revert to default : remove from cwrite (i.e. site-config)
1047 elif (answer == "/"):
1048 cwrite.delete(category_id,variable_id)
1049 print ("%s reverted to %s" %(varname,default_value))
1051 elif (answer == ">"):
1053 raise Exception ('NextCategory')
1055 print "No support for next category"
1057 variable['value'] = answer
1058 cwrite.set(category,variable)
1061 def prompt_variables_all (cdef, cread, cwrite, show_comments):
1063 for (category_id, (category, variables)) in cread.variables().iteritems():
1064 print ("========== Category = %s" % category_id.upper())
1065 for variable in variables.values():
1067 newvar = prompt_variable (cdef, cread, cwrite, category, variable,
1068 show_comments, True)
1069 except Exception, inst:
1070 if (str(inst) == 'NextCategory'): break
1073 except Exception, inst:
1074 if (str(inst) == 'BailOut'): return
1077 def prompt_variables_category (cdef, cread, cwrite, cid, show_comments):
1081 print ("========== Category = %s" % CID)
1082 for vid in list_category(cdef,cid):
1083 (category,variable) = cdef.locate_varname(vid.upper())
1084 newvar = prompt_variable (cdef, cread, cwrite, category, variable,
1085 show_comments, False)
1086 except Exception, inst:
1087 if (str(inst) == 'BailOut'): return
1090 ####################
1091 def show_variable (cdef, cread, cwrite,
1092 category, variable,show_value,show_comments):
1093 assert category.has_key('id')
1094 assert variable.has_key('id')
1096 category_id = category ['id']
1097 variable_id = variable['id']
1099 default_value = get_value(cdef,category_id,variable_id)
1100 current_value = get_current_value(cread,cwrite,category_id,variable_id)
1101 varname = get_varname (cread,category_id, variable_id)
1103 print_name_comments (cdef, category_id, variable_id)
1105 print "%s = %s" % (varname,current_value)
1107 print "%s" % (varname)
1109 def show_variables_all (cdef, cread, cwrite, show_value, show_comments):
1110 for (category_id, (category, variables)) in cread.variables().iteritems():
1111 print ("========== Category = %s" % category_id.upper())
1112 for variable in variables.values():
1113 show_variable (cdef, cread, cwrite,
1114 category, variable,show_value,show_comments)
1116 def show_variables_category (cdef, cread, cwrite, cid, show_value,show_comments):
1119 print ("========== Category = %s" % CID)
1120 for vid in list_category(cdef,cid):
1121 (category,variable) = cdef.locate_varname(vid.upper())
1122 show_variable (cdef, cread, cwrite, category, variable,
1123 show_value,show_comments)
1125 ####################
1126 re_mainloop_0arg="^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
1127 re_mainloop_1arg="^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
1128 matcher_mainloop_0arg=re.compile(re_mainloop_0arg)
1129 matcher_mainloop_1arg=re.compile(re_mainloop_1arg)
1131 def mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config):
1135 answer = raw_input("Enter command (u for usual changes, w to save, ? for help) ").strip()
1138 except KeyboardInterrupt:
1142 if (answer == "") or (answer in "?hH"):
1143 print mainloop_usage
1145 groups_parse = matcher_mainloop_0arg.match(answer)
1148 command = groups_parse.group('command')
1151 groups_parse = matcher_mainloop_1arg.match(answer)
1153 command = groups_parse.group('command')
1154 arg=groups_parse.group('arg')
1156 print ("Unknown command >%s< -- use h for help" % answer)
1159 show_comments=command.isupper()
1160 command=command.lower()
1166 variables=list_category (cdef,arg)
1168 # category_id as the category name
1169 # variables as the list of variable names
1173 (category,variable)=cdef.locate_varname(arg)
1175 # category/variable as output by locate_varname
1178 print "%s: no such category or variable" % arg
1181 if (command in "qQ"):
1182 # todo check confirmation
1184 elif (command == "w"):
1186 # Confirm that various constraints are met before saving file.
1187 validate_variables = g_configuration.get('validate_variables',{})
1188 validated_variables = cwrite.verify(cdef, cread, validate_variables)
1189 validator = g_configuration.get('validator',noop_validator)
1190 validator(validated_variables)
1191 cwrite.save(site_config)
1192 except ConfigurationException, e:
1193 print "Save failed due to a configuration exception: %s" % e
1196 print traceback.print_exc()
1197 print ("Could not save -- fix write access on %s" % site_config)
1199 print ("Wrote %s" % site_config)
1200 consolidate(default_config, site_config, consolidated_config)
1201 print ("You might want to type 'r' (restart %s), 'R' (reload %s) or 'q' (quit)" % \
1203 elif (command == "u"):
1204 global usual_variables
1206 for varname in usual_variables:
1207 (category,variable) = cdef.locate_varname(varname)
1208 if not (category is None and variable is None):
1209 prompt_variable(cdef, cread, cwrite, category, variable, False)
1210 except Exception, inst:
1211 if (str(inst) != 'BailOut'):
1213 elif (command == "r"):
1215 elif (command == "R"):
1217 elif (command == "c"):
1218 print_categories(cread)
1219 elif (command in "eE"):
1221 prompt_variables_all(cdef, cread, cwrite,show_comments)
1222 elif mode == 'CATEGORY':
1223 prompt_variables_category(cdef,cread,cwrite,category_id,show_comments)
1224 elif mode == 'VARIABLE':
1226 prompt_variable (cdef,cread,cwrite,category,variable,
1227 show_comments,False)
1228 except Exception, inst:
1229 if (str(inst) != 'BailOut'):
1231 elif (command in "vVsSlL"):
1232 show_value=(command in "sSlL")
1233 (c1,c2,c3) = (cdef, cread, cwrite)
1234 if (command in "lL"):
1235 (c1,c2,c3) = (cwrite,cwrite,cwrite)
1237 show_variables_all(c1,c2,c3,show_value,show_comments)
1238 elif mode == 'CATEGORY':
1239 show_variables_category(c1,c2,c3,category_id,show_value,show_comments)
1240 elif mode == 'VARIABLE':
1241 show_variable (c1,c2,c3,category,variable,show_value,show_comments)
1243 print ("Unknown command >%s< -- use h for help" % answer)
1245 ####################
1246 # creates directory for file if not yet existing
1247 def check_dir (config_file):
1248 dirname = os.path.dirname (config_file)
1249 if (not os.path.exists (dirname)):
1251 os.makedirs(dirname,0755)
1253 print "Cannot create dir %s due to %s - exiting" % (dirname,e)
1256 if (not os.path.exists (dirname)):
1257 print "Cannot create dir %s - exiting" % dirname
1260 print "Created directory %s" % dirname
1262 ####################
1263 def optParserSetup(configuration):
1264 parser = OptionParser(usage=usage(), version="%prog 1.0" + release_rev + release_url )
1265 parser.set_defaults(config_dir=configuration['config_dir'],
1266 service=configuration['service'],
1267 usual_variables=configuration['usual_variables'])
1268 parser.add_option("","--configdir",dest="config_dir",help="specify configuration directory")
1269 parser.add_option("","--service",dest="service",help="specify /etc/init.d style service name")
1270 parser.add_option("","--usual_variable",dest="usual_variables",action="append", help="add a usual variable")
1273 def main(command,argv,configuration):
1274 global g_configuration
1275 g_configuration=configuration
1277 parser = optParserSetup(configuration)
1278 (config,args) = parser.parse_args()
1280 parser.error("too many arguments")
1282 configuration['service']=config.service
1283 configuration['usual_variables']=config.usual_variables
1284 configuration['config_dir']=config.config_dir
1285 # add in new usual_variables defined on the command line
1286 for usual_variable in config.usual_variables:
1287 if usual_variable not in configuration['usual_variables']:
1288 configuration['usual_variables'].append(usual_variable)
1290 # intialize configuration
1291 init_configuration()
1293 (default_config,site_config,consolidated_config) = (def_default_config, def_site_config, def_consolidated_config)
1295 default_config=args[0]
1299 consolidated_config=args[2]
1301 for c in (default_config,site_config,consolidated_config):
1305 # the default settings only - read only
1306 cdef = PLCConfiguration(default_config)
1308 # in effect : default settings + local settings - read only
1309 cread = PLCConfiguration(default_config)
1311 except ConfigurationException, e:
1312 print ("Error %s in default config file %s" %(e,default_config))
1315 print traceback.print_exc()
1316 print ("default config files %s not found, is myplc installed ?" % default_config)
1320 # local settings only, will be modified & saved
1321 cwrite=PLCConfiguration()
1324 cread.load(site_config)
1325 cwrite.load(site_config)
1327 cwrite = PLCConfiguration()
1329 mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config)
1332 if __name__ == '__main__':
1334 if len(sys.argv) > 1 and sys.argv[1] in ['build', 'install', 'uninstall']:
1335 from distutils.core import setup
1336 setup(py_modules=["plc_config"])