Tagging module MyPLC - MyPLC-4.3-12
[myplc.git] / plc-config-tty
index 9bb8abe..2c2505b 100755 (executable)
@@ -1,98 +1,66 @@
-#!/usr/bin/python
+#!/bin/env python
 
 # Interactively prompts for variable values
 # expected arguments are
 # command -d [default-xml [custom-xml [ consolidated-xml ]]]
-#
-# -d is for the myplc-devel package
+
+# we use 3 instances of PLCConfiguration throughout:
+# cdef : models the defaults, from plc_default.xml
+# cread : merged from plc_default & configs/site.xml
+# cwrite : site.xml + pending changes
 
 import sys
 import os
 import re
 import readline
-import getopt
+import traceback
+from optparse import OptionParser
 
 from plc_config import PLCConfiguration
+from plc_config import ConfigurationException
 
 ####################
-release_id = "$Id: plc-config-tty,v 1.5 2006/08/08 16:59:23 thierry Exp $"
+release_id = "$Id$"
 release_rev = "$Revision$"
-
-def init_flavour (flavour):
-    global service
-    global common_variables
-    if (flavour == "devel"):
-        service="plc-devel"
-        common_variables=("PLC_DEVEL_FEDORA_URL",
-                          "PLC_DEVEL_CVSROOT")
-        config_dir = "/plc/devel/data/etc/planetlab"
-    else:
-        service="plc"
-        common_variables=("PLC_NAME",
-                          "PLC_ROOT_USER",
-                          "PLC_ROOT_PASSWORD",
-                          "PLC_MAIL_SUPPORT_ADDRESS",
-                          "PLC_DB_HOST",
-                          "PLC_API_HOST",
-                          "PLC_WWW_HOST",
-                          "PLC_BOOT_HOST",
-                          "PLC_NET_DNS1",
-                          "PLC_NET_DNS2")
-        config_dir = "/etc/planetlab"
-    global def_default_config
-    def_default_config= "%s/default_config.xml" % config_dir
-    global def_site_config
-    def_site_config = "%s/configs/site.xml" % config_dir
-    global def_consolidated_config
-    def_consolidated_config = "%s/plc_config.xml" % config_dir
-
-    global mainloop_usage
-    mainloop_usage= """Available commands:
- Uppercase versions give variables comments, when available
--u/U\t\t\tEdit usual variables
--w\t\t\tWrite & consolidate
--r\t\t\tRestart %s service
--q\t\t\tQuit (without saving)
--h/?\t\t\tThis help
----
-l/L [<cat>|<var>]\tShow Locally modified variables/values
--s/S [<cat>|<var>]\tShow variables/values (all, in category, single)
--e/E [<cat>|<var>]\tEdit variables (all, in category, single)
----
--c\t\t\tList categories
--v/V [<cat>|<var>]List Variables (all, in category, single)
----
-Typical usage involves: u, [l,] w, r, q
-""" % service
+release_url = "$URL$"
+
+def validator (validated_variables):
+    maint_user = validated_variables["PLC_API_MAINTENANCE_USER"]
+    root_user = validated_variables["PLC_ROOT_USER"]
+    if maint_user == root_user:
+        raise ConfigurationException("PLC_API_MAINTENANCE_USER=%s cannot be the same as PLC_ROOT_USER=%s"%(maint_user,root_user))
+
+# historically we could also configure the devel pkg....
+globals = {
+    'def_default_config' : "/etc/planetlab/default_config.xml",
+    'def_site_config' : "/etc/planetlab/configs/site.xml",
+    'def_consolidated_config' : "/etc/planetlab/plc_config.xml",
+    'usual_variables': ["PLC_NAME",
+                        "PLC_SHORTNAME",
+                        "PLC_SLICE_PREFIX",
+                        "PLC_ROOT_USER",
+                        "PLC_ROOT_PASSWORD",
+                        "PLC_MAIL_ENABLED",
+                        "PLC_MAIL_SUPPORT_ADDRESS",
+                        "PLC_DB_HOST",
+                        "PLC_API_HOST",
+                        "PLC_WWW_HOST",
+                        "PLC_BOOT_HOST",
+                        "PLC_NET_DNS1",
+                        "PLC_NET_DNS2",
+                        ],
+    }
 
 def usage ():
-    command_usage="Usage: %s [-d] [-v] [default-xml [site-xml [consolidated-xml]]]"% sys.argv[0]
-    init_flavour ("boot")
-    command_usage +="""
-  -v shows version and exits
-\t default-xml defaults to %s
-\t site-xml defaults to %s
-\t consolidated-xml defaults to %s""" % (def_default_config,def_site_config, def_consolidated_config)
-    command_usage += """
-  Unless you specify the -d option, meaning you want to configure
-  myplc-devel instead of regular myplc, in which case""" 
-    init_flavour ("devel")
+    command_usage="%prog [options] [default-xml [site-xml [consolidated-xml]]]"
+    global globals
     command_usage +="""
 \t default-xml defaults to %s
 \t site-xml defaults to %s
-\t consolidated-xml defaults to %s""" % (def_default_config,def_site_config, def_consolidated_config)
-    print(command_usage)
-    sys.exit(1)
-
-####################
-variable_usage= """Edit Commands :
-#\tShow variable comments
-.\tStops prompting, return to mainloop
-/\tCleans any site-defined value, reverts to default
-=\tShows default value
->\tSkips to next category
-?\tThis help
-"""
+\t consolidated-xml defaults to %s""" % (globals['def_default_config'],
+                                         globals['def_site_config'], 
+                                         globals['def_consolidated_config'])
+    return command_usage
 
 ####################
 def get_value (config,  category_id, variable_id):
@@ -174,13 +142,16 @@ def consolidate (default_config, site_config, consolidated_config):
         return
     print ("Merged\n\t%s\nand\t%s\ninto\t%s"%(default_config,site_config,
                                               consolidated_config))
+
+def reload_service ():
+    os.system("set -x ; service plc reload")
         
 ####################
-def restart_plc ():
-    print ("==================== Stopping %s" % service)
-    os.system("service %s stop" % service)
-    print ("==================== Starting %s" % service)
-    os.system("service %s start" % service)
+def restart_service ():
+    print ("==================== Stopping plc" )
+    os.system("service plc stop" )
+    print ("==================== Starting plc" )
+    os.system("service plc start")
 
 ####################
 def prompt_variable (cdef, cread, cwrite, category, variable,
@@ -204,6 +175,9 @@ def prompt_variable (cdef, cread, cwrite, category, variable,
             answer = raw_input(prompt).strip()
         except EOFError :
             raise Exception ('BailOut')
+        except KeyboardInterrupt:
+            print "\n"
+            raise Exception ('BailOut')
 
         # no change
         if (answer == "") or (answer == current_value):
@@ -213,6 +187,14 @@ def prompt_variable (cdef, cread, cwrite, category, variable,
         elif (answer == "#"):
             print_name_comments(cread,category_id,variable_id)
         elif (answer == "?"):
+            variable_usage = """Edit Commands :
+#\tShow variable comments
+.\tStops prompting, return to mainloop
+/\tCleans any site-defined value, reverts to default
+=\tShows default value
+>\tSkips to next category
+?\tThis help
+"""
             print variable_usage.strip()
         elif (answer == "="):
             print ("%s defaults to %s" %(varname,default_value))
@@ -296,7 +278,7 @@ def show_variables_category (cdef, cread, cwrite, cid, show_value,show_comments)
                        show_value,show_comments)
 
 ####################
-re_mainloop_0arg="^(?P<command>[uUwrqlLsSeEcvVhH\?])[ \t]*$"
+re_mainloop_0arg="^(?P<command>[uUwrRqlLsSeEcvVhH\?])[ \t]*$"
 re_mainloop_1arg="^(?P<command>[sSeEvV])[ \t]+(?P<arg>\w+)$"
 matcher_mainloop_0arg=re.compile(re_mainloop_0arg)
 matcher_mainloop_1arg=re.compile(re_mainloop_1arg)
@@ -307,7 +289,28 @@ def mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_con
             answer = raw_input("Enter command (u for usual changes, w to save, ? for help) ").strip()
         except EOFError:
             answer =""
+        except KeyboardInterrupt:
+            print "\nBye"
+            sys.exit()
+
         if (answer == "") or (answer in "?hH"):
+            mainloop_usage = """Available commands:
+ Uppercase versions give variables comments, when available
+ u/U\t\t\tEdit usual variables
+ w/W\t\t\tWrite / Write & reload
+ r\t\t\tRestart plc service
+ q\t\t\tQuit (without saving)
+ h/?\t\t\tThis help
+---
+ l/L [<cat>|<var>]\tShow Locally modified variables/values
+ s/S [<cat>|<var>]\tShow variables/values (all, in category, single)
+ e/E [<cat>|<var>]\tEdit variables (all, in category, single)
+---
+ c\t\t\tList categories
+ v/V [<cat>|<var>]List Variables (all, in category, single)
+---
+Typical usage involves: u, [l,] w, r, q
+"""
             print mainloop_usage
             continue
         groups_parse = matcher_mainloop_0arg.match(answer)
@@ -349,25 +352,37 @@ def mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_con
         if (command in "qQ"):
             # todo check confirmation
             return
-        elif (command in "wW"):
+        elif (command == "w"):
+            global defined_flavour
             try:
+                # Confirm that various constraints are met before saving file.
+                validate_variables = {"PLC_API":"MAINTENANCE_USER","PLC":"ROOT_USER"}
+                validated_variables = cwrite.verify(cdef, cread, validate_variables)
+                validator(validated_variables)
                 cwrite.save(site_config)
+            except ConfigurationException, e:
+                print "Save failed due to a configuration exception: %s" % e
+                break;
             except:
+                print traceback.print_exc()
                 print ("Could not save -- fix write access on %s" % site_config)
                 break
-            print ("Wrote %s" % site_config)
+            print "Wrote",site_config
             consolidate(default_config, site_config, consolidated_config)
-            print ("You might want to type 'r' (restart plc) or 'q' (quit)")
+            print "You might want to type 'r' (restart plc), 'R' (reload plc) or 'q' (quit)"
         elif (command == "u"):
             try:
-                for varname in common_variables:
+                global globals
+                for varname in globals['usual_variables']:
                     (category,variable) = cdef.locate_varname(varname)
                     prompt_variable(cdef, cread, cwrite, category, variable, False)
             except Exception, inst:
                 if (str(inst) != 'BailOut'):
                     raise
         elif (command == "r"):
-            restart_plc()
+            restart_service()
+        elif (command == "R"):
+            reload_service()
         elif (command == "c"):
             print_categories(cread)
         elif (command in "eE"):
@@ -376,8 +391,12 @@ def mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_con
             elif mode == 'CATEGORY':
                 prompt_variables_category(cdef,cread,cwrite,category_id,show_comments)
             elif mode == 'VARIABLE':
-                prompt_variable (cdef,cread,cwrite,category,variable,
-                                 show_comments,False)
+                try:
+                    prompt_variable (cdef,cread,cwrite,category,variable,
+                                     show_comments,False)
+                except Exception, inst:
+                    if (str(inst) != 'BailOut'):
+                        raise
         elif (command in "vVsSlL"):
             show_value=(command in "sSlL")
             (c1,c2,c3) = (cdef, cread, cwrite)
@@ -393,54 +412,41 @@ def mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_con
             print ("Unknown command >%s< -- use h for help" % answer)
 
 ####################
+# creates directory for file if not yet existing
 def check_dir (config_file):
     dirname = os.path.dirname (config_file)
     if (not os.path.exists (dirname)):
-        print "Config file %s located under a non-existing directory" % config_file
-        answer=raw_input("Want to create %s [y]/n ? " % dirname)
-        answer = answer.lower()
-        if (answer == 'n'):
-            print "Cannot proceed - good bye"
+        try:
+            os.makedirs(dirname,0755)
+        except OSError, e:
+            print "Cannot create dir %s for file %s - error=[%s] - exiting" % (dirname,config_file,e)
+            sys.exit(1)
+            
+        if (not os.path.exists (dirname)):
+            print "Cannot create dir %s for file %s - exiting" % (dirname,config_file)
             sys.exit(1)
         else:
-            os.makedirs(dirname,0755)
-            if (not os.path.exists (dirname)):
-                print "Cannot create dir %s - exiting" % dirname
-                sys.exit(1)
-            else:
-                print "Created directory %s" % dirname
-
+            print "Created directory %s" % dirname
                 
 ####################
 def main ():
 
-    command=sys.argv[0]
-    argv = sys.argv[1:]
-
-    save = True
-    # default is myplc (non -devel) unless -d is specified
-    init_flavour("boot")
-    optlist,list = getopt.getopt(argv,":dhv")
-    for opt in optlist:
-        if opt[0] == "-h":
-            usage()
-        if opt[0] == "-v":
-            print ("This is %s - %s" %(command,release_id))
-            sys.exit(1)
-        if opt[0] == "-d":
-            init_flavour("devel")
-            argv=argv[1:]
-
-    if len(argv) == 0:
-        (default_config,site_config,consolidated_config) = (def_default_config, def_site_config, def_consolidated_config)
-    elif len(argv) == 1:
-        (default_config,site_config,consolidated_config) = (argv[0], def_site_config, def_consolidated_config)
-    elif len(argv) == 2:
-        (default_config, site_config,consolidated_config)  = (argv[0], argv[1], def_consolidated_config)
-    elif len(argv) == 3:
-        (default_config, site_config,consolidated_config)  = argv
-    else:
-        usage()
+    parser = OptionParser(usage=usage(), version="%prog " + release_rev + release_url)
+    (config,args) = parser.parse_args()
+    if len(args)>3:
+        parser.error("too many arguments")
+
+    # use any provided arg as advertised
+    global globals
+    (default_config,site_config,consolidated_config) = (globals['def_default_config'],
+                                                        globals['def_site_config'],
+                                                        globals['def_consolidated_config'])
+    if len(args) >= 1:
+        default_config=args[0]
+    if len(args) >= 2:
+        site_config=args[1]
+    if len(args) == 3:
+        consolidated_config=args[2]
 
     for c in (default_config,site_config,consolidated_config):
         check_dir (c)
@@ -452,10 +458,15 @@ def main ():
         # in effect : default settings + local settings - read only
         cread = PLCConfiguration(default_config)
 
+    except ConfigurationException, e:
+        print ("Error %s in default config file %s" %(e,default_config))
+        return 1
     except:
-        print ("default config files not found, is myplc installed ?")
+        print traceback.print_exc()
+        print ("default config files %s not found, is myplc installed ?" % default_config)
         return 1
 
+
     # local settings only, will be modified & saved
     cwrite=PLCConfiguration()
     
@@ -465,7 +476,7 @@ def main ():
     except:
         cwrite = PLCConfiguration()
 
-    mainloop (cdef, cread, cwrite,default_config, site_config, consolidated_config)
+    mainloop (cdef, cread, cwrite, default_config, site_config, consolidated_config)
     return 0
 
 if __name__ == '__main__':