Tweaks
[nodemanager.git] / accounts.py
index 6bffdbe..df33977 100644 (file)
@@ -10,12 +10,20 @@ code.  For no particular reason, TYPE is divided hierarchically by
 periods; at the moment the only convention is that all sliver accounts
 have type that begins with sliver.
 
+There are any number of race conditions that may result from the fact
+that account names are not unique over time.  Moreover, it's a bad
+idea to perform lengthy operations while holding the database lock.
+In order to deal with both of these problems, we use a worker thread
+for each account name that ever exists.  On 32-bit systems with large
+numbers of accounts, this may cause the NM process to run out of
+*virtual* memory!  This problem may be remedied by decreasing the
+maximum stack size.
 """
 
 import Queue
 import os
 import pwd
-from grp import getgrnam
+import grp
 import threading
 
 import logger
@@ -41,7 +49,6 @@ def register_class(acct_class):
 
 # private account name -> worker object association and associated lock
 name_worker_lock = threading.Lock()
-# dict of account_name: <Worker Object>
 name_worker = {}
 
 def allpwents():
@@ -55,9 +62,7 @@ def get(name):
     """Return the worker object for a particular username.  If no such object exists, create it first."""
     name_worker_lock.acquire()
     try:
-        if name not in name_worker: 
-            logger.verbose("Accounts:get(%s) new Worker" % name)
-            name_worker[name] = Worker(name)
+        if name not in name_worker: name_worker[name] = Worker(name)
         return name_worker[name]
     finally: name_worker_lock.release()
 
@@ -80,16 +85,35 @@ class Account:
         logger.verbose('%s: in accounts:configure'%self.name)
         new_keys = rec['keys']
         if new_keys != self.keys:
-            self.keys = new_keys
-            dot_ssh = '/home/%s/.ssh' % self.name
-            if not os.access(dot_ssh, os.F_OK): os.mkdir(dot_ssh)
+            # get the unix account info
+            gid = grp.getgrnam("slices")[2]
+            pw_info = pwd.getpwnam(self.name)
+            uid = pw_info[2]
+            pw_dir = pw_info[5]
+
+            # write out authorized_keys file and conditionally create
+            # the .ssh subdir if need be.
+            dot_ssh = os.path.join(pw_dir,'.ssh')
+            if not os.path.isdir(dot_ssh):
+                if not os.path.isdir(pw_dir):
+                    logger.verbose('WARNING: homedir %s does not exist for %s!'%(pw_dir,self.name))
+                    os.mkdir(pw_dir)
+                    os.chown(pw_dir, uid, gid)
+                os.mkdir(dot_ssh)
+
+            auth_keys = os.path.join(dot_ssh,'authorized_keys')
+            tools.write_file(auth_keys, lambda f: f.write(new_keys))
+
+            # set access permissions and ownership properly
             os.chmod(dot_ssh, 0700)
-            tools.write_file(dot_ssh + '/authorized_keys', lambda f: f.write(new_keys))
-            logger.log('%s: installing ssh keys' % self.name)
-            user = pwd.getpwnam(self.name)[2]
-            group = getgrnam("slices")[2]
-            os.chown(dot_ssh, user, group)
-            os.chown(dot_ssh + '/authorized_keys', user, group)
+            os.chown(dot_ssh, uid, gid)
+            os.chmod(auth_keys, 0600)
+            os.chown(auth_keys, uid, gid)
+
+            # set self.keys to new_keys only when all of the above ops succeed
+            self.keys = new_keys
+
+            logger.log('%s: installed ssh keys' % self.name)
 
     def start(self, delay=0): pass
     def stop(self): pass
@@ -127,7 +151,7 @@ class Worker:
     def stop(self): self._acct.stop()
 
     def is_running(self): 
-        if self._acct.is_running():
+        if (self._acct != None) and self._acct.is_running():
             status = True
         else:
             status = False