[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[minion-cvs] Add functionality to upgrade server homedirs.



Update of /home/minion/cvsroot/src/minion/lib/mixminion/server
In directory moria.mit.edu:/tmp/cvs-serv23333/lib/mixminion/server

Modified Files:
	ServerKeys.py ServerMain.py 
Log Message:
Add functionality to upgrade server homedirs.

If all my code were somehow bug-free, 0.0.4 would now be finished.
Time to start testing and debugging!



Index: ServerKeys.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/ServerKeys.py,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -d -r1.24 -r1.25
--- ServerKeys.py	27 May 2003 17:24:49 -0000	1.24
+++ ServerKeys.py	28 May 2003 04:53:34 -0000	1.25
@@ -59,9 +59,6 @@
        hash logs for a mixminion server.
 
        DOCDOC
-
-       FFFF We need a way to generate keys as needed, not just a month's
-       FFFF worth of keys up front.
        """
     ## Fields:
     # homeDir: server home directory
@@ -349,14 +346,7 @@
             name = keyset.keyname
             LOG.info("Removing%s key %s (valid from %s through %s)",
                      expiryStr, name, formatDate(va), formatDate(vu-3600))
-            dirname = os.path.join(self.keyDir, "key_"+name)
-            files = [ os.path.join(dirname,f)
-                      for f in os.listdir(dirname) ]
-            hashFiles = [ os.path.join(self.hashDir, "hash_"+name) ,
-                          os.path.join(self.hashDir, "hash_"+name+"_jrnl") ]
-            files += [ f for f in hashFiles if os.path.exists(f) ]
-            secureDelete(files, blocking=1)
-            os.rmdir(dirname)
+            keyset.delete()
 
         self.checkKeys()
 
@@ -499,7 +489,7 @@
     # descFile: filename of this keyset's server descriptor.
     #
     # packetKey, mmtpKey: This server's actual short-term keys.
-    # DOCDOC serverinfo, validAfter, validUntil,published(File)?
+    # DOCDOC serverinfo, validAfter, validUntil,published(File)?, keydir
     def __init__(self, keyroot, keyname, hashroot):
         """Load a set of keys named "keyname" on a server where all keys
            are stored under the directory "keyroot" and hashlogs are stored
@@ -508,7 +498,7 @@
         self.keyname = keyname
         self.hashroot= hashroot
 
-        keydir  = os.path.join(keyroot, "key_"+keyname)
+        self.keydir = keydir = os.path.join(keyroot, "key_"+keyname)
         self.hashlogFile = os.path.join(hashroot, "hash_"+keyname)
         self.packetKeyFile = os.path.join(keydir, "mix.key")
         self.mmtpKeyFile = os.path.join(keydir, "mmtp.key")
@@ -521,6 +511,25 @@
         self.published = os.path.exists(self.publishedFile)
         if not os.path.exists(keydir):
             createPrivateDir(keydir)
+
+    def delete(self):
+        """DOCDOC"""
+        files = [self.packetKeyFile,
+                 self.mmtpKeyFile,
+                 self.certFile,
+                 self.descFile,
+                 self.publishedFile]
+        files = [f for f in files if os.path.exists(f)]
+        hashdir, name = os.path.split(self.hashlogFile)
+        if os.path.exists(hashdir):
+            start1 = name+"."
+            start2 = name+"_"
+            for fn in os.listdir(hashdir):
+                if fn.startswith(start1) or fn.startswith(start2):
+                    files.append(os.path.join(hashdir, fn))
+
+        secureDelete(files, blocking=1)
+        os.rmdir(self.keydir)
 
     def load(self, password=None):
         """Read the short-term keys from disk.  Must be called before

Index: ServerMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/server/ServerMain.py,v
retrieving revision 1.57
retrieving revision 1.58
diff -u -d -r1.57 -r1.58
--- ServerMain.py	27 May 2003 17:24:49 -0000	1.57
+++ ServerMain.py	28 May 2003 04:53:34 -0000	1.58
@@ -8,6 +8,8 @@
    See the "MixminionServer" class for more information about how it
    all works. """
 
+#XXXX make usage messages have the same format.
+
 ## Directory layout:
 #    MINION_HOME/work/queues/incoming/ [Queue of received,unprocessed pkts]
 #                            mix/ [Mix pool]
@@ -30,7 +32,7 @@
 #                     key_0002/...
 #                conf/miniond.conf [configuration file]
 #                stats [DOCDOC]
-#                
+#                version [DOCDOC]
 
 # FFFF Support to put keys/queues in separate directories.
 
@@ -63,7 +65,44 @@
 from bisect import insort
 from mixminion.Common import LOG, LogStream, MixError, MixFatalError,\
      UIError, ceilDiv, createPrivateDir, formatBase64, formatTime, \
-     installSIGCHLDHandler, Lockfile, secureDelete, waitForChildren
+     installSIGCHLDHandler, Lockfile, readFile, secureDelete, waitForChildren,\
+     writeFile
+
+#DOCDOC
+# For backward-incompatible changes only.
+SERVER_HOMEDIR_VERSION = "1001"
+
+def getHomedirVersion(config):
+    homeDir = config['Server']['Homedir']
+    versionFile = os.path.join(homeDir, "version")
+    if not os.path.exists(homeDir):
+        return None
+    elif not os.path.exists(versionFile):
+        dirVersion = "1000"
+    else:
+        dirVersion = readFile(f).strip()
+
+    return dirVersion
+
+def checkHomedirVersion(config):
+    dirVersion = getDirVersion(config)
+
+    if dirVersion is None:
+        return None
+    elif dirVersion != SERVER_HOMEDIR_VERSION:
+        if float(dirVersion) < float(SERVER_HOMEDIR_VERSION):
+            print >>sys.stderr, """\
+This server's files are stored in an older format, and are not compatible
+with this version of the mixminion server.  To upgrade, run:
+     'mixminion server-upgrade'."""
+            sys.exit(0)
+        else:
+            print >>sys.stderr, """\
+This server's file are stored in format which this version of mixminion
+is too old to recognize."""
+            sys.exit(0)
+
+    return 1
 
 class IncomingQueue(mixminion.server.ServerQueue.Queue):
     """A Queue to accept packets from incoming MMTP connections,
@@ -548,7 +587,13 @@
 
         self.config = config
         homeDir = config['Server']['Homedir']
+
+        exists = checkHomedirVersion(config)
+
         createPrivateDir(homeDir)
+        if not exists:
+            writeFile(os.path.join(homeDir, "version"),
+                      SERVER_HOMEDIR_VERSION, 0644)
 
         # Lock file.
         self.lockFile = Lockfile(os.path.join(homeDir, "lock"))
@@ -867,15 +912,23 @@
     print _SERVER_USAGE %cmd
     sys.exit(0)
 
-def configFromServerArgs(cmd, args):
+def configFromServerArgs(cmd, args, usage=None):
     #XXXX
     options, args = getopt.getopt(args, "hf:", ["help", "config="])
     if args:
-        usageAndExit(cmd)
+        if usage:
+            print usage
+            sys.exit(0)
+        else:
+            usageAndExit(cmd)
     configFile = None
     for o,v in options:
         if o in ('-h', '--help'):
-            usageAndExit(cmd)
+            if usage:
+                print usage
+                sys.exit(0)
+            else:
+                usageAndExit(cmd)
         if o in ('-f', '--config'):
             configFile = v
 
@@ -905,7 +958,7 @@
         print >>sys.stderr, "Error in configuration file %r"%configFile
         print >>sys.stderr, str(e)
         sys.exit(1)
-    return None #suppress pychecker warning
+    return None #never reached; here to suppress pychecker warning
 
 #----------------------------------------------------------------------
 def runServer(cmd, args):
@@ -978,6 +1031,94 @@
     sys.exit(0)
 
 #----------------------------------------------------------------------
+_UPGRADE_USAGE = """\
+Usage: mixminion server-upgrade [options]
+Options:
+  -h, --help:                Print this usage message and exit.
+  -f <file>, --config=<file> Use a configuration file other than
+                                /etc/mixminiond.conf
+""".strip()
+
+def runUpgrade(cmd, args):
+    """Remove all keys server descriptors for old versions of this
+       server.  If any are found, nuke the keysets, """
+
+    config = configFromServerArgs(cmd, args, usage=_UPGRADE_USAGE)    
+    assert config
+
+    mixminion.Common.configureShredCommand(config)
+    mixminion.Crypto.init_crypto(config)
+
+    curVersion = getHomedirVersion(config)
+    if curVersion is None:
+        print "Server homedir doesn't exist."
+        return
+    elif curVersion == SERVER_HOMEDIR_VERSION:
+        print "Server is current; No need to upgrade."
+        return
+    elif float(curVersion) > float(SERVER_HOMEDIR_VERSION):
+        print "Server homedir uses unrecognized version; I can't downgrade."
+        return
+
+    assert curVersion == "1000"
+
+    homeDir = config['Server']['Homedir']
+    keyDir = os.path.join(homeDir, 'keys')
+    hashDir = os.path.join(hashDir, 'hashlogs')
+    keysets = []
+    if not os.path.exists(keyDir):
+        print >>sys.stderr, "No server keys to upgrade."
+    else:
+        for fn in os.listdir(keyDir):
+            if fn.startswith("key_"):
+                name = fn[4:]
+                keysets.append(mixminion.server.ServerKeys.ServerKeyset(
+                    keyDir, name, hashDir))
+
+    errors = 0
+    remove = 0
+    keep = 0
+    for keyset in keysets:
+        try:
+            keyset.load()
+            keep += 1
+        except ConfigError, e:
+            errors += 1
+            if e.startswith("Unrecognized descriptor version: 0.1"):
+                print "Removing old keyset %s"%keyset.keyname
+                keyset.delete()
+            else:
+                print "Unrecognized error from keyset %s: %s" % (
+                    keyset.keyname, str(e))
+                
+
+
+    # Now we need to clean out all the old queues -- the messages in them
+    # are incompatible.
+    queueDirs = [ os.path.join(homeDir, 'work', 'queues', 'incoming'),
+                  os.path.join(homeDir, 'work', 'queues', 'mix'),
+                  os.path.join(homeDir, 'work', 'queues', 'outgoing') ]
+    deliver = os.path.join(homeDir, 'work', 'queues', 'deliver')
+    if os.path.exists(deliver):
+        for fn in os.listdir(deliver):
+            if os.path.isdir(os.path.join(deliver,fn)):
+                queueDirs.append(os.path.join(deliver,fn))
+
+    print "Dropping obsolete messages from queues (no upgrade; sorry!)"
+
+    for qd in queueDirs:
+        if not os.path.exists(qd): continue
+        files = os.listdir(qd)
+        print "   (Deleting %s files from %s)" %(len(files,qd))
+        secureDelete([os.path.join(qd,f) for f in files])
+
+    print "Homedir is upgraded"
+
+    writeFile(os.path.join(homeDir, 'version'),
+              SERVER_HOMEDIR_VERSION, 0644)
+
+
+#----------------------------------------------------------------------
 _PRINT_STATS_USAGE = """\
 Usage: mixminion server-stats [options]
 Options:
@@ -987,7 +1128,8 @@
 
 def printServerStats(cmd, args):
     #XXXX
-    config = configFromServerArgs(cmd, args)
+    config = configFromServerArgs(cmd, args, _PRINT_STATS_USAGE)
+    checkHomedirVersion(config)
     _signalServer(config, 1)
     EventStats.configureLog(config)
     EventStats.log.dump(sys.stdout)
@@ -1001,20 +1143,11 @@
 """.strip()
 
 def signalServer(cmd, args):
-    options, args = getopt.getopt(args, "hf:", ["help", "config="])
-    usage = 0
-    # XXXX Refactor this and configFromServerArgs to raise UsageError
-    if args:
-        usage = 1
-    configFile = None
-    for o,v in options:
-        if o in ('-h', '--help'):
-            usageAndExit(cmd)
-        elif o in ('-f', '--config'):
-            configFile = v
-
+    config = configFromServerArgs(cmd, args, usage=_SIGNAL_SERVER_USAGE)
     LOG.setMinSeverity("ERROR")
-    config = readConfigFile(configFile)
+
+    checkHomedirVersion(config)
+    
     if cmd.endswith("stop-server") or cmd.endswith("server-stop"):
         reload = 0
     else:
@@ -1069,26 +1202,9 @@
 """.strip()
 
 def runRepublish(cmd, args):
-    options, args = getopt.getopt(args, "hf:",
-                                  ["help", "config=",])
-    
-    
-    # FFFF password-encrypted keys
-    # FFFF Ability to fill gaps
-    # FFFF Ability to generate keys with particular start/end intervals
-    keys=1
-    usage=0
-    configFile = None
-    for opt,val in options:
-        if opt in ('-h', '--help'):
-            usage=1
-        elif opt in ('-f', '--config'):
-            configFile = val
-    if usage:
-        print _REPUBLISH_USAGE
-        sys.exit(1)
+    config = configFromServerArgs(cmd, args, usage=_REPUBLISH_USAGE)
 
-    config = readConfigFile(configFile)
+    checkHomedirVersion(config)    
 
     LOG.setMinSeverity("INFO")
     mixminion.Crypto.init_crypto(config)