Merge from Branch.
authorFaiyaz Ahmed <faiyaza@cs.princeton.edu>
Tue, 24 Mar 2009 19:47:07 +0000 (19:47 +0000)
committerFaiyaz Ahmed <faiyaza@cs.princeton.edu>
Tue, 24 Mar 2009 19:47:07 +0000 (19:47 +0000)
accounts.py
conf_files.py
controller.py
logger.py
net.py
sliver_vs.py
sm.py

index d540368..480129c 100644 (file)
@@ -1,8 +1,8 @@
 """Functionality common to all account classes.
 
-Each subclass of Account must provide 6 methods: create() and
-destroy(), which are static; configure(), is_running(), start(), and stop(),
-which are not.  configure() takes a record as its only argument, and does
+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
 that it uses; and TYPE, a string that is used by the account creation
@@ -10,6 +10,14 @@ 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
index fd926a5..94560d6 100644 (file)
@@ -5,18 +5,17 @@ import os
 import pwd
 import sha
 import string
-import threading
 
 import curlwrapper
 import logger
 import tools
 import xmlrpclib
+from config import Config 
 
 class conf_files:
-    def __init__(self, config, noscripts=False):
-        self.config = config
+    def __init__(self, noscripts=False):
+        self.config = Config()
         self.noscripts = noscripts
-        self.cond = threading.Condition()
         self.data = None
 
     def checksum(self, path):
@@ -78,37 +77,20 @@ class conf_files:
         if self.system(cf_rec['postinstall_cmd']): self.system(err_cmd)
 
     def run_once(self, data):
-        for f in data['conf_files']:
-            try: self.update_conf_file(f)
-            except: logger.log_exc()
+        if data.has_key("conf_files"):
+            for f in data['conf_files']:
+                try: self.update_conf_file(f)
+                except: logger.log_exc()
+        else: logger.log("conf_files:  No conf_files found or API failure.  Skipping")
 
-    def run(self):
-        while True:
-            self.cond.acquire()
-            while self.data == None: self.cond.wait()
-            data = self.data
-            self.data = None
-            self.cond.release()
-            self.run_once(data)
 
-    def callback(self, data):
-        if data != None:
-            self.cond.acquire()
-            self.data = data
-            self.cond.notify()
-            self.cond.release()
-
-main = None
-
-def start(options, config):
-    global main
-    main = conf_files(config)
-    tools.as_daemon_thread(main.run)
+def start(options, config): pass
 
 def GetSlivers(data):
-    global main
-    assert main is not None
-    return main.callback(data)
+    logger.log("conf_files: Running.")
+    cf = conf_files()
+    cf.run_once(data)
+    logger.log("conf_files: Done.")
 
 if __name__ == '__main__':
     import optparse
@@ -119,7 +101,6 @@ if __name__ == '__main__':
     (options, args) = parser.parse_args()
 
     # Load /etc/planetlab/plc_config
-    from config import Config
     config = Config(options.config)
 
     # Load /etc/planetlab/session
@@ -132,6 +113,6 @@ if __name__ == '__main__':
     from plcapi import PLCAPI
     plc = PLCAPI(config.plc_api_uri, config.cacert, auth = session)
 
-    main = conf_files(config, options.noscripts)
+    main = conf_files(options.noscripts)
     data = plc.GetSlivers()
     main.run_once(data)
index 17268d2..1fc4847 100644 (file)
@@ -4,6 +4,7 @@ import accounts
 import logger
 import tools
 from pwd import getpwnam
+from grp import getgrnam
 
 class Controller(accounts.Account):
     SHELL = '/usr/bin/forward_api_calls'  # tunneling shell
@@ -12,7 +13,8 @@ class Controller(accounts.Account):
     @staticmethod
     def create(name, vref = None):
         add_shell(Controller.SHELL)
-        logger.log_call('/usr/sbin/useradd', '-p', '*', '-s', Controller.SHELL, name)
+        group = getgrnam("slices")[2]
+        logger.log_call('/usr/sbin/useradd', '-p', '*', '-g', str(group), '-s', Controller.SHELL, name)
 
     @staticmethod
     def destroy(name): logger.log_call('/usr/sbin/userdel', '-r', name)
index 0b083ca..3fedde9 100644 (file)
--- a/logger.py
+++ b/logger.py
@@ -43,7 +43,7 @@ def log(msg,level=LOG_NODE):
 
 def log_call(*args):
     log('running command %s' % ' '.join(args))
-    try: subprocess.call(args, close_fds=True)
+    try: subprocess.check_call(args, close_fds=True)
     except: log_exc()
 
 def log_exc(name = None):
diff --git a/net.py b/net.py
index 810490c..0c8b357 100644 (file)
--- a/net.py
+++ b/net.py
@@ -1,6 +1,7 @@
 #
 # $Id$
 #
+
 """network configuration"""
 
 # system provided modules
@@ -15,10 +16,10 @@ import bwlimit, logger, iptables
 def GetSlivers(plc, data, config):
     logger.verbose("net:GetSlivers called.")
     InitInterfaces(plc, data) # writes sysconfig files.
-    if 'OVERRIDES' in dir(config):
+    if 'OVERRIDES' in dir(config): 
         if config.OVERRIDES.get('net_max_rate') == '-1':
             logger.log("net: Slice and node BW Limits disabled.")
-            if len(bwlimit.tc("class show dev eth0")):
+            if len(bwlimit.tc("class show dev eth0")): 
                 logger.verbose("*** DISABLING NODE BW LIMITS ***")
                 bwlimit.stop()
         else:
@@ -31,6 +32,8 @@ def GetSlivers(plc, data, config):
 
 
 def InitNodeLimit(data):
+    if not 'networks' in data: return
+
     # query running network interfaces
     devs = sioc.gifconf()
     ips = dict(zip(devs.values(), devs.keys()))
@@ -41,9 +44,11 @@ def InitNodeLimit(data):
     for network in data['networks']:
         # Get interface name preferably from MAC address, falling
         # back on IP address.
-        if macs.has_key(network['mac'].lower()):
-            dev = macs[network['mac'].lower()]
-        elif ips.has_key(network['ip']):
+        hwaddr=network['mac']
+        if hwaddr <> None: hwaddr=hwaddr.lower()
+        if hwaddr in macs:
+            dev = macs[network['mac']]
+        elif network['ip'] in ips:
             dev = ips[network['ip']]
         else:
             logger.log('%s: no such interface with address %s/%s' % (network['hostname'], network['ip'], network['mac']))
@@ -70,6 +75,8 @@ def InitNodeLimit(data):
             # again, or vice-versa.
 
 def InitI2(plc, data):
+    if not 'groups' in data: return
+
     if "Internet2" in data['groups']:
         logger.log("This is an Internet2 node.  Setting rules.")
         i2nodes = []
@@ -93,6 +100,8 @@ def InitI2(plc, data):
             os.popen("/sbin/iptables -t mangle " + cmd)
 
 def InitNAT(plc, data):
+    if not 'networks' in data: return
+    
     # query running network interfaces
     devs = sioc.gifconf()
     ips = dict(zip(devs.values(), devs.keys()))
@@ -104,9 +113,11 @@ def InitNAT(plc, data):
     for network in data['networks']:
         # Get interface name preferably from MAC address, falling
         # back on IP address.
-        if macs.has_key(network['mac']):
-            dev = macs[network['mac'].lower()]
-        elif ips.has_key(network['ip']):
+        hwaddr=network['mac']
+        if hwaddr <> None: hwaddr=hwaddr.lower()
+        if hwaddr in macs:
+            dev = macs[network['mac']]
+        elif network['ip'] in ips:
             dev = ips[network['ip']]
         else:
             logger.log('%s: no such interface with address %s/%s' % (network['hostname'], network['ip'], network['mac']))
@@ -116,7 +127,7 @@ def InitNAT(plc, data):
             settings = plc.GetInterfaceTags({'interface_tag_id': network['interface_tag_ids']})
         except:
             continue
-        # XXX arbitrary names
+
         for setting in settings:
             if setting['category'].upper() != 'FIREWALL':
                 continue
@@ -139,8 +150,7 @@ def InitNAT(plc, data):
     ipt.commit()
 
 def InitInterfaces(plc, data):
-    if not 'networks' in data:
-        return
+    if not 'networks' in data: return
     plnet.InitInterfaces(logger, plc, data)
 
 def start(options, config):
index ed09821..5573984 100644 (file)
@@ -17,9 +17,9 @@ don't have to guess if there is a running process or not.
 """
 
 import errno
+import traceback
 import os, os.path
 import time
-import commands
 
 import vserver
 
@@ -70,20 +70,6 @@ class Sliver_VS(accounts.Account, vserver.VServer):
         self.initscriptchanged = False
         self.configure(rec)
 
-    _root_context_arch=None
-    @staticmethod 
-    def root_context_arch():
-        if not Sliver_VS._root_context_arch:
-            Sliver_VS._root_context_arch=commands.getoutput("uname -i")
-        return Sliver_VS._root_context_arch
-
-    @staticmethod
-    def personality (arch):
-        personality="linux32"
-        if arch.find("64")>=0:
-            personality="linux64"
-        return personality
-
     @staticmethod
     def create(name, vref = None):
         logger.verbose('Sliver_VS:create - name=%s'%name)
@@ -122,9 +108,7 @@ class Sliver_VS(accounts.Account, vserver.VServer):
             refname="-".join( (pldistro,fcdistro,arch) )
 
             # check the template exists -- there's probably a better way..
-            if os.path.isdir ("/vservers/.vref/%s"% vref): refname = vref
-
-            if not os.path.isdir ("/vservers/.vref/%s"% refname):
+            if not os.path.isdir ("/vservers/.vref/%s"%refname):
                 logger.log("%s (%s) : vref %s not found, using default %s"%(
                         name,vref,refname,default))
                 refname=default
@@ -140,19 +124,24 @@ class Sliver_VS(accounts.Account, vserver.VServer):
             refname="default"
             arch="i386"
         except:
-            import traceback
             logger.log("%s (%s) : unexpected error follows - using 'default'"%(name,vref))
             logger.log(traceback.format_exc())
             refname="default"
             arch="i386"
             
+        def personality (arch):
+            personality="linux32"
+            if arch.find("64")>=0:
+                personality="linux64"
+            return personality
+
         logger.log_call('/usr/sbin/vuseradd', '-t', refname, name)
         # export slicename to the slice in /etc/slicename
         file('/vservers/%s/etc/slicename' % name, 'w').write(name)
         # set personality: only if needed (if arch's differ)
-        if Sliver_VS.root_context_arch() != arch:
-            file('/etc/vservers/%s/personality' % name, 'w').write(Sliver_VS.personality(arch))
-            logger.log('%s: set personality to %s'%(name,Sliver_VS.personality(arch)))
+        if tools.root_context_arch() != arch:
+            file('/etc/vservers/%s/personality' % name, 'w').write(personality(arch))
+            logger.log('%s: set personality to %s'%(name,personality(arch)))
 
     @staticmethod
     def destroy(name): logger.log_call('/usr/sbin/vuserdel', name)
@@ -168,8 +157,7 @@ class Sliver_VS(accounts.Account, vserver.VServer):
             self.initscript = new_initscript
             self.initscriptchanged = True
 
-        # install ssh keys
-        accounts.Account.configure(self, rec)  
+        accounts.Account.configure(self, rec)  # install ssh keys
 
     def start(self, delay=0):
         if self.rspec['enabled'] > 0:
diff --git a/sm.py b/sm.py
index 62e49d1..d8a0aca 100644 (file)
--- a/sm.py
+++ b/sm.py
@@ -15,7 +15,7 @@ import accounts
 import api
 import api_calls
 import database
-import controller 
+import controller
 import logger
 import sliver_vs
 import string,re
@@ -100,7 +100,7 @@ def GetSlivers(data, fullupdate=True):
         ## 'Type' isn't returned by GetSlivers() for whatever reason.  We're overloading
         ## instantiation here, but i suppose its the ssame thing when you think about it. -FA
         # Handle nm controller here
-        if rec['instantiation'].lower() == 'nm-controller': 
+        if rec['instantiation'].lower() == 'nm-controller':
             rec.setdefault('type', attr_dict.get('type', 'controller.Controller'))
         else:
             rec.setdefault('type', attr_dict.get('type', 'sliver.VServer'))