Merge changeset 11123 from the trunk
[nodemanager.git] / plugins / codemux.py
diff --git a/plugins/codemux.py b/plugins/codemux.py
new file mode 100644 (file)
index 0000000..ce69e4a
--- /dev/null
@@ -0,0 +1,133 @@
+# $Id$
+# $URL$
+
+"""Codemux configurator.  Monitors slice attributes and configures CoDemux to mux port 80 based on HOST field in HTTP request.  Forwards to localhost port belonging to configured slice."""
+
+import logger
+import os
+import vserver
+from sets import Set
+from config import Config
+
+CODEMUXCONF="/etc/codemux/codemux.conf"
+
+def start(options, config):
+    pass
+
+
+def GetSlivers(data):
+    """
+    For each sliver with the codemux attribute, parse out "host,port" 
+    and make entry in conf.  Restart service after.
+    """
+    logger.log("codemux:  Starting.", 2)
+    # slices already in conf
+    slicesinconf = parseConf()
+    # slices that need to be written to the conf
+    codemuxslices = {}
+    
+    # XXX Hack for planetflow
+    if slicesinconf.has_key("root"): _writeconf = False
+    else: _writeconf = True
+
+    # Parse attributes and update dict of scripts
+    for sliver in data['slivers']:
+        for attribute in sliver['attributes']:
+            if attribute['tagname'] == 'codemux':
+                # add to conf.  Attribute is [host, port]
+                params = {'host': attribute['value'].split(",")[0], 
+                          'port': attribute['value'].split(",")[1]}
+                try:
+                    # Check to see if sliver is running.  If not, continue
+                    if vserver.VServer(sliver['name']).is_running():
+                        # Check if new or needs updating
+                        if (sliver['name'] not in slicesinconf.keys()) \
+                        or (params not in slicesinconf.get(sliver['name'], [])):
+                            logger.log("codemux:  Updaiting slice %s using %s" % \
+                                (sliver['name'], params['host']))
+                            #  Toggle write.
+                            _writeconf = True
+                        # Add to dict of codemuxslices.  Make list to support more than one
+                        # codemuxed host per slice.
+                        codemuxslices.setdefault(sliver['name'],[])
+                        codemuxslices[sliver['name']].append(params)
+                except:
+                    logger.log("codemux:  sliver %s not running yet.  Deferring."\
+                                % sliver['name'])
+                    pass
+
+    # Remove slices from conf that no longer have the attribute
+    for deadslice in Set(slicesinconf.keys()) - Set(codemuxslices.keys()):
+        # XXX Hack for root slice
+        if deadslice != "root": 
+            logger.log("codemux:  Removing %s" % deadslice)
+            _writeconf = True 
+
+    if _writeconf:  writeConf(sortDomains(codemuxslices))
+
+def writeConf(slivers, conf = CODEMUXCONF):
+    '''Write conf with default entry up top. Elements in [] should have lower order domain names first. Restart service.'''
+    f = open(conf, "w")
+    # This needs to be the first entry...
+    try: 
+        f.write("* root 1080 %s\n" % Config().PLC_PLANETFLOW_HOST)
+    except AttributeError: 
+        logger.log("codemux:  Can't find PLC_CONFIG_HOST in config. Using PLC_API_HOST")
+        f.write("* root 1080 %s\n" % Config().PLC_API_HOST)
+    # Sort items for like domains
+    for mapping in slivers:
+        for (host, params) in mapping.iteritems():
+            if params['slice'] == "root":  continue
+            f.write("%s %s %s\n" % (host, params['slice'], params['port']))
+    f.truncate()
+    f.close()
+    try:  restartService()
+    except:  logger.log_exc()
+
+def sortDomains(slivers):
+    '''Given a dict of {slice: {domainname, port}}, return array of slivers with lower order domains first'''
+    dnames = {} # {host: slice}
+    for (slice, params) in slivers.iteritems():
+        for mapping in params:
+            dnames[mapping['host']] = {"slice":slice, "port": mapping['port']}
+    hosts = dnames.keys()
+    # sort by length
+    hosts.sort(key=str.__len__)
+    # longer first
+    hosts.reverse()
+    # make list of slivers
+    sortedslices = []
+    for host in hosts: sortedslices.append({host: dnames[host]})
+    
+    return sortedslices
+        
+def parseConf(conf = CODEMUXCONF):
+    '''Parse the CODEMUXCONF and return dict of slices in conf. {slice: (host,port)}'''
+    slicesinconf = {} # default
+    try: 
+        f = open(conf)
+        for line in f.readlines():
+            if line.startswith("#") \
+            or (len(line.split()) > 4) \
+            or (len(line.split()) < 3):
+                continue
+            (host, slice, port) = line.split()[:3]
+            logger.log("codemux:  found %s in conf" % slice, 2)
+            slicesinconf.setdefault(slice, [])
+            slicesinconf[slice].append({"host": host, "port": port})
+        f.close()
+    except IOError: logger.log_exc()
+    return slicesinconf
+
+def restartService():
+    logger.log("codemux:  Restarting codemux service")
+    os.system("/etc/init.d/codemux stop")
+    f = os.popen("/sbin/pidof codemux")
+    tmp = f.readlines()
+    f.close()
+    if len(tmp) > 0: 
+        pids = tmp[0].rstrip("\n").split()
+        for pid in pids:
+            logger.log("codemux:  Killing stalled pid %s" % pid, 2)
+            os.kill(pid, 9)
+    os.system("/etc/init.d/codemux start")