Added ReCreate. Also added try catch to api eval of rpc method.
[nodemanager.git] / accounts.py
index 8363e7f..47e7af5 100644 (file)
@@ -1,12 +1,14 @@
 """Functionality common to all account classes.
 
 """Functionality common to all account classes.
 
-Each subclass of Account must provide five methods: create(),
-destroy(), configure(), start(), and stop().  In addition, it must
+Each subclass of Account must provide five methods: create() and
+destroy(), which are static; configure(), start(), and stop(), which
+are not.  configure(), which takes a record as its only argument, does
+things like set up ssh keys.  In addition, an Account subclass must
 provide static member variables SHELL, which contains the unique shell
 provide static member variables SHELL, which contains the unique shell
-that it uses; and TYPE, which contains a description of the type that
-it uses.  TYPE is divided hierarchically by periods; at the moment the
-only convention is that all sliver accounts have type that begins with
-sliver.
+that it uses; and TYPE, a string that is used by the account creation
+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
 
 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
@@ -27,6 +29,12 @@ import logger
 import tools
 
 
 import tools
 
 
+# When this variable is true, start after any ensure_created
+Startingup = False
+# Cumulative delay for starts when Startingup is true
+csd_lock = threading.Lock()
+cumstartdelay = 0
+
 # shell path -> account class association
 shell_acct_class = {}
 # account type -> account class association
 # shell path -> account class association
 shell_acct_class = {}
 # account type -> account class association
@@ -42,9 +50,12 @@ def register_class(acct_class):
 name_worker_lock = threading.Lock()
 name_worker = {}
 
 name_worker_lock = threading.Lock()
 name_worker = {}
 
+def allpwents():
+    return [pw_ent for pw_ent in pwd.getpwall() if pw_ent[6] in shell_acct_class]
+
 def all():
     """Return the names of all accounts on the system with recognized shells."""
 def all():
     """Return the names of all accounts on the system with recognized shells."""
-    return [pw_ent[0] for pw_ent in pwd.getpwall() if pw_ent[6] in shell_acct_class]
+    return [pw_ent[0] for pw_ent in allpwents()]
 
 def get(name):
     """Return the worker object for a particular username.  If no such object exists, create it first."""
 
 def get(name):
     """Return the worker object for a particular username.  If no such object exists, create it first."""
@@ -59,10 +70,11 @@ class Account:
     def __init__(self, rec):
         self.name = rec['name']
         self.keys = ''
     def __init__(self, rec):
         self.name = rec['name']
         self.keys = ''
+        self.initscriptchanged = False
         self.configure(rec)
 
     @staticmethod
         self.configure(rec)
 
     @staticmethod
-    def create(name): abstract
+    def create(name, vref = None): abstract
     @staticmethod
     def destroy(name): abstract
 
     @staticmethod
     def destroy(name): abstract
 
@@ -74,11 +86,12 @@ class Account:
             dot_ssh = '/home/%s/.ssh' % self.name
             def do_installation():
                 if not os.access(dot_ssh, os.F_OK): os.mkdir(dot_ssh)
             dot_ssh = '/home/%s/.ssh' % self.name
             def do_installation():
                 if not os.access(dot_ssh, os.F_OK): os.mkdir(dot_ssh)
+                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)
             tools.fork_as(self.name, do_installation)
 
                 tools.write_file(dot_ssh + '/authorized_keys', lambda f: f.write(new_keys))
             logger.log('%s: installing ssh keys' % self.name)
             tools.fork_as(self.name, do_installation)
 
-    def start(self): pass
+    def start(self, delay=0): pass
     def stop(self): pass
 
 
     def stop(self): pass
 
 
@@ -98,25 +111,33 @@ class Worker:
 
     def ensure_created(self, rec):
         """Cause the account specified by <rec> to exist if it doesn't already."""
 
     def ensure_created(self, rec):
         """Cause the account specified by <rec> to exist if it doesn't already."""
-        self._q.put((self._ensure_created, rec.copy()))
+        self._q.put((self._ensure_created, rec.copy(), Startingup))
 
 
-    def _ensure_created(self, rec):
+    def _ensure_created(self, rec, startingup):
         curr_class = self._get_class()
         next_class = type_acct_class[rec['type']]
         if next_class != curr_class:
             self._destroy(curr_class)
             self._create_sem.acquire()
         curr_class = self._get_class()
         next_class = type_acct_class[rec['type']]
         if next_class != curr_class:
             self._destroy(curr_class)
             self._create_sem.acquire()
-            try: next_class.create(self.name)
+            try: next_class.create(self.name, rec['vref'])
             finally: self._create_sem.release()
         if not isinstance(self._acct, next_class): self._acct = next_class(rec)
         else: self._acct.configure(rec)
             finally: self._create_sem.release()
         if not isinstance(self._acct, next_class): self._acct = next_class(rec)
         else: self._acct.configure(rec)
-        if next_class != curr_class: self._acct.start()
+        if startingup:
+            csd_lock.acquire()
+            global cumstartdelay
+            delay = cumstartdelay
+            cumstartdelay += 2
+            csd_lock.release()
+            self._acct.start(delay=delay)
+        elif next_class != curr_class or self._acct.initscriptchanged:
+            self._acct.start()
 
     def ensure_destroyed(self): self._q.put((self._ensure_destroyed,))
     def _ensure_destroyed(self): self._destroy(self._get_class())
 
 
     def ensure_destroyed(self): self._q.put((self._ensure_destroyed,))
     def _ensure_destroyed(self): self._destroy(self._get_class())
 
-    def start(self): self._q.put((self._start,))
-    def _start(self): self._acct.start()
+    def start(self, delay=0): self._q.put((self._start, delay))
+    def _start(self, d): self._acct.start(delay=d)
 
     def stop(self): self._q.put((self._stop,))
     def _stop(self): self._acct.stop()
 
     def stop(self): self._q.put((self._stop,))
     def _stop(self): self._acct.stop()
@@ -134,8 +155,10 @@ class Worker:
         return shell_acct_class[shell]
 
     def _run(self):
         return shell_acct_class[shell]
 
     def _run(self):
+        """Repeatedly pull commands off the queue and execute.  If memory usage becomes an issue, it might be wise to terminate after a while."""
         while True:
             try:
                 cmd = self._q.get()
                 cmd[0](*cmd[1:])
         while True:
             try:
                 cmd = self._q.get()
                 cmd[0](*cmd[1:])
-            except: logger.log_exc()
+            except:
+                logger.log_exc(self.name)