[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[minion-cvs] Document directory stuff; refactor a bit; add a path CL...



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

Modified Files:
	DirMain.py ServerList.py 
Log Message:
Document directory stuff; refactor a bit; add a path CLI that doesn't suck.

ClientMain, ServerList, Crypto, ServerInfo, DirMain:
- Document new code.

ClientMain:
- Add a path CLI that, while a little harder to code, looks easier to use.
       mixminion client --nHops=6 --path='Joe,*,Moria1:Moria2'
  is certainly easier than
       mixminion client --entry-servers=Joe --exit-servers=Moria1,Moria2 \
          --nHops=6 --nSwap=4.
- Refactor read-config-file logic.

Common, Config, ServerInfo, ServerList:
- Add readPossiblyGzippedFile to common; refactor accordingly.

ServerInfo, test:
- Add an isSupersededBy method to ServerInfo
- Debug new ServerInfo methods

test:
- Test more new ServerInfo methods
- Test prng.pick



Index: DirMain.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/directory/DirMain.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- DirMain.py	3 Jan 2003 05:14:47 -0000	1.1
+++ DirMain.py	3 Jan 2003 08:25:48 -0000	1.2
@@ -4,7 +4,7 @@
 """mixminion.directory.DirMain
 
    CLI for mixminion directory generation.
-   """
+"""
 
 __all__ = [ ]
 
@@ -21,15 +21,17 @@
 
 USAGE = """%s -d <directory> command 
    Where 'command' is one of:
-      import <serverinfo>
-      import-new <serverinfo>
-      generate
-      export <filename>
-      remove <nickname>
-      fingerprint"""
+      import <serverinfo>      [Import a descriptor for a known server]
+      import-new <serverinfo>  [Import a descriptor for a new server]
+      generate                 [Generate and sign a new directory]
+      export <filename>        [Export the most recently generated directory]
+      remove <nickname>        [Remove a server from storage]
+      fingerprint              [Return the fingerprint of this directory's pk]
+"""
 
 def getIdentity(baseDir):
-    "DOCDOC"
+    """Load the identity key stored under the base directory, creating it
+       if necessary."""
     createPrivateDir(baseDir)
     fname = os.path.join(baseDir, "identity")
     if not os.path.exists(fname):
@@ -41,9 +43,8 @@
         return pk_PEM_load(fname)
     
 def usageAndExit(cmd):
-    "DOCDOC"
+    """Print a usage message and exit"""
     print >>sys.stderr, USAGE%cmd
-    raise "N"
     sys.exit(1)
 
 def cmd_import(cmd, base, rest):
@@ -71,7 +72,6 @@
     print >>sys.stderr, "Directory generated."
 
 def cmd_export(cmd, base, rest):
-    "DOCDOC"
     if len(rest) != 1: usageAndExit(cmd)
     lst = ServerList(base)
     fname = lst.getDirectoryFilename()

Index: ServerList.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/directory/ServerList.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- ServerList.py	3 Jan 2003 05:14:47 -0000	1.2
+++ ServerList.py	3 Jan 2003 08:25:48 -0000	1.3
@@ -21,31 +21,49 @@
 from mixminion.Crypto import pk_encode_public_key, pk_same_public_key
 from mixminion.Common import IntervalSet, LOG, MixError, createPrivateDir, \
      formatBase64, formatDate, formatFnameTime, formatTime, previousMidnight, \
-     stringContains
+     readPossiblyGzippedFile, stringContains
 from mixminion.Config import ConfigError
 from mixminion.ServerInfo import ServerDirectory, ServerInfo, \
      _getDirectoryDigestImpl
 
-# Layout:
-#  basedir
-#     servers/
-#       nickname-dateinserted.
-#     archive/
-#     reject/
-#     directory
-#     dirArchive/
-
 class ServerList:
-    "DOCDOC"
-    ##Fields: DOCDOC
-    #  baseDir
-    #  serverDir
-    #  rejectDir,
-    #  archiveDir
-    #  servers (filename->ServerInfo)
-    #  serversByNickname (nickname -> [filename, filename,...])
+    """A ServerList holds a set of server descriptors for use in generating
+       directories.  It checks new descriptors for consistency with old ones
+       as they are inserted.  It will reject any server if:
+          -- it is expired (Valid-Until in the past)
+          -- it is superseded (For all time it is valid, a more-recently-
+             published descriptor is also valid.)
+          -- it is inconsistent (We already know a descriptor for this
+             nickname, with a different identity key.)
+             [FFFF This check will become stricter in the future.]
+          
+       This implementation isn't terribly optimized, but there's no need to
+       optimize it until we have far more descriptors to worry about.
+    """
+    ##Fields:
+    #  baseDir: Base directory of this list
+    #  serverDir: Directory where we store active descriptors.
+    #  rejectDir: Directory where we store invalid descriptors.
+    #  archiveDir: Directory where we store old descriptors
+    #  servers: Map from filename within <serverDir> to ServerInfo objects.
+    #  serversByNickname: A map from server nickname to lists of filenames
+    #       within <serverDir>
+    ##Layout:
+    #  basedir
+    #     servers/
+    #          nickname-dateinserted.N ...
+    #     archive/
+    #          nickname-dateinserted.N ...
+    #     reject/
+    #          nickname-dateinserted.N ...
+    #     directory
+    #     dirArchive/
+    #          dir-dategenerated.N ...
+    #     identity
     def __init__(self, baseDir):
-        "DOCDOC"
+        """Initialize a ServerList to store servers under baseDir/servers,
+           creating directories as needed.
+        """
         self.baseDir = baseDir
         self.serverDir = os.path.join(self.baseDir, "servers")
         self.rejectDir = os.path.join(self.baseDir, "reject")
@@ -60,14 +78,19 @@
         self.rescan()
         
     def importServerInfo(self, server, knownOnly=0):
-        "DOCDOC"
+        """Insert a ServerInfo into the list.  If the server is expired, or
+           superseded, or inconsistent, raise a MixError. 
+           
+           server -- a string containing the descriptor, or the name of a
+               file containing the descriptor (possibly gzip'd)
+           knownOnly -- if true, raise MixError is we don't already have
+               a descriptor with this nickname.
+        """
         # Raises ConfigError, MixError, 
         if stringContains(server, "[Server]"):
             contents = server
         else:
-            f = open(server, 'r')
-            contents = f.read()
-            f.close()
+            contents = readPossiblyGzippedFile(fname)
 
         server = ServerInfo(string=contents, assumeValid=0)
 
@@ -93,10 +116,10 @@
                 if oldServer['Server']['Digest'] == server['Server']['Digest']:
                     raise MixError("Server descriptor already inserted.")
             # Okay -- make sure that this server isn't superseded.
-            if self._serverIsSupersededBy(server, 
+            if server.isSupersededBy(
                [ self.servers[fn] for fn in self.serversByNickname[nickname]]):
                 raise MixError("Server descriptor is superseded")
-
+        
         newFile = nickname+"-"+formatFnameTime()
         f, newFile = _openUnique(os.path.join(self.serverDir, newFile))
         newFile = os.path.split(newFile)[1]
@@ -108,7 +131,7 @@
         self.serversByNickname.setdefault(nickname, []).append(newFile)
 
     def expungeServersByNickname(self, nickname):
-        "DOCDOC"
+        """Forcibly remove all servers named <nickname>"""
         LOG.info("Removing all servers named %s", nickname)
         if not self.serversByNickname.has_key(nickname):
             LOG.info("  (No such servers exist)")
@@ -125,7 +148,10 @@
     def generateDirectory(self,
                           startAt, endAt, extraTime,
                           identityKey, publicationTime=None):
-        "DOCDOC"
+        """Generate and sign a new directory, to be effective from <startAt>
+           through <endAt>.  It includes all servers that are valid at
+           any time between <startAt> and <endAt>+>extraTime>.  The directory
+           is signed with <identityKey> """
         if publicationTime is None:
             publicationTime = time.time()
         if previousMidnight(startAt) >= previousMidnight(endAt):
@@ -177,7 +203,7 @@
             for s in parsed.getServers():
                 foundDigests[s['Server']['Digest']] = 1
             assert foundDigests == includedDigests
-
+            
         f = open(os.path.join(self.baseDir, "directory"), 'w')
         f.write(directory)
         f.close()
@@ -188,22 +214,19 @@
         f.close()
 
     def getDirectoryFilename(self):
-        "DOCDOC"
+        """Return the filename of the most recently generated directory"""
         return os.path.join(self.baseDir, "directory")
 
     def clean(self, now=None):
-        "DOCDOC"
-        # A server needs to be cleaned out if it is no longer valid,
-        # or if its future validity range is wholly covered by other, more
-        # recently published descriptors for the same server.
-
+        """Remove all expired or superceded servers from the active directory.
+        """
         # This algorithm is inefficient: O(N_descs * N_descs_per_nickname).
         # We're just going to ignore that.
         if now is None:
             now = time.time()
 
-        removed = {}
-        beforeNow = IntervalSet([(0, time.time())])
+        removed = {} # Map from filename->whyRemoved
+        # Find all superseded servers
         for name, servers in self.serversByNickname.items():
             servers = [ (self.servers[fn]['Server']['Published'], 
                         fn, self.servers[fn]) for fn in servers ]
@@ -211,10 +234,10 @@
             fns = [ fn for _, fn, _ in servers]
             servers = [ s for _, _, s  in servers ]
             for idx in range(len(servers)):
-                if self._serverIsSupersededBy(servers[idx],
-                                              servers[idx+1:]):
+                if servers[idx].isSupersededBy(servers[idx+1:]):
                     removed[fns[idx]] = "superceded"
 
+        # Find all expired servers.
         for fn, s in self.servers.items():
             if removed.has_key(fn):
                 continue
@@ -240,6 +263,7 @@
                      removed[fn], fn, name)
             del removed[fn]
  
+        # Now, do the actual removing.
         for fn, why in removed.items():
             LOG.info("Removing %s descriptor %s", why, fn)
             os.rename(os.path.join(self.serverDir, fn),
@@ -250,8 +274,9 @@
         self.__buildNicknameMap()        
     
     def rescan(self):
-        "DOCDOC"
+        """Reconstruct this ServerList object's internal state."""
         self.servers = {}
+        # First, build self.servers
         for filename in os.listdir(self.serverDir):
             path = os.path.join(self.serverDir, filename)
             try:
@@ -262,32 +287,28 @@
                 LOG.warn(" (Error was: %s)", str(e))
                 os.rename(path, os.path.join(self.rejectDir, filename))
 
+        # Then, rebuild self.serversByNickname
         self.__buildNicknameMap()
 
     def __buildNicknameMap(self):
-        "DOCDOC"
+        """Helper method. Regenerate self.serversByNickname from
+           self.servers"""
         self.serversByNickname = {}
         for fn, server in self.servers.items():
             nickname = server.getNickname()
             self.serversByNickname.setdefault(nickname, []).append(fn)
 
-    def _serverIsSupersededBy(self, server, others):
-        "DOCDOC"
-        validity = server.getIntervalSet()
-        for s in others:
-            if server.isNewerThan(s):
-                continue
-            validity -= s.getIntervalSet()
-        return validity.isEmpty()
-        
-def _openUnique(fname):
-    "DOCDOC"
+def _openUnique(fname, mode='w'):
+    """Helper function. Returns a file open for writing into the file named
+       'fname'.  If fname already exists, opens 'fname.1' or 'fname.2' or
+       'fname.3' or so on."""
+    # ???? Should this go into common?
     base, rest = os.path.split(fname)
     idx = 0
     while 1:
         try:
             fd = os.open(fname, os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0600)
-            return os.fdopen(fd, 'w'), fname
+            return os.fdopen(fd, mode), fname
         except OSError:
             pass
         idx += 1