(no commit message)
[nodemanager.git] / plugins / codemux.py
1 # $Id$
2 # $URL$
3
4 """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."""
5
6 import logger
7 import os
8 import vserver
9 from sets import Set
10 from config import Config
11
12 CODEMUXCONF="/etc/codemux/codemux.conf"
13
14 def start(options, conf):
15     logger.log("codemux: plugin starting up...")
16
17 def GetSlivers(data, config, plc = None):
18     """
19     For each sliver with the codemux attribute, parse out "host,port" 
20     and make entry in conf.  Restart service after.
21     """
22     if 'OVERRIDES' in dir(config):
23         if config.OVERRIDES.get('codemux') == '-1':
24             logger.log("codemux:  Disabled", 2)
25             stopService()
26             return
27
28     logger.log("codemux:  Starting.", 2)
29     # slices already in conf
30     slicesinconf = parseConf()
31     # slices that need to be written to the conf
32     codemuxslices = {}
33     
34     # XXX Hack for planetflow
35     if slicesinconf.has_key("root"): _writeconf = False
36     else: _writeconf = True
37
38     # Parse attributes and update dict of scripts
39     if 'slivers' not in data:
40         logger.log_missing_data("codemux.GetSlivers", 'slivers')
41         return
42     for sliver in data['slivers']:
43         for attribute in sliver['attributes']:
44             if attribute['tagname'] == 'codemux':
45                 # add to conf.  Attribute is [host, port]
46                 parts = attribute['value'].split(",")
47                 if len(parts)<2:
48                     logger.log("codemux: attribute value (%s) for codemux not separated by comma. Skipping."%attribute['value'])
49                     continue
50                 if len(parts) == 3:
51                     ip = parts[2]
52                 else:
53                     ip = ""
54                 params = {'host': parts[0], 'port': parts[1], 'ip': ip}
55
56                 try:
57                     # Check to see if sliver is running.  If not, continue
58                     if vserver.VServer(sliver['name']).is_running():
59                         # Check if new or needs updating
60                         if (sliver['name'] not in slicesinconf.keys()) \
61                         or (params not in slicesinconf.get(sliver['name'], [])):
62                             logger.log("codemux:  Updaiting slice %s using %s" % \
63                                 (sliver['name'], params['host']))
64                             #  Toggle write.
65                             _writeconf = True
66                         # Add to dict of codemuxslices.  Make list to support more than one
67                         # codemuxed host per slice.
68                         codemuxslices.setdefault(sliver['name'],[])
69                         codemuxslices[sliver['name']].append(params)
70                 except:
71                     logger.log("codemux:  sliver %s not running yet.  Deferring."\
72                                 % sliver['name'])
73                     pass
74
75     # Remove slices from conf that no longer have the attribute
76     for deadslice in Set(slicesinconf.keys()) - Set(codemuxslices.keys()):
77         # XXX Hack for root slice
78         if deadslice != "root": 
79             logger.log("codemux:  Removing %s" % deadslice)
80             _writeconf = True 
81
82     if _writeconf:  writeConf(sortDomains(codemuxslices))    
83     # ensure the service is running
84     startService()
85
86
87 def writeConf(slivers, conf = CODEMUXCONF):
88     '''Write conf with default entry up top. Elements in [] should have lower order domain names first. Restart service.'''
89     f = open(conf, "w")
90     # This needs to be the first entry...
91     try: 
92         f.write("* root 1080 %s\n" % Config().PLC_PLANETFLOW_HOST)
93     except AttributeError: 
94         logger.log("codemux:  Can't find PLC_CONFIG_HOST in config. Using PLC_API_HOST")
95         f.write("* root 1080 %s\n" % Config().PLC_API_HOST)
96     # Sort items for like domains
97     for mapping in slivers:
98         for (host, params) in mapping.iteritems():
99             if params['slice'] == "root":  continue
100             f.write("%s %s %s %s\n" % (host, params['slice'], params['port'], params['ip']))
101     f.truncate()
102     f.close()
103     try:  restartService()
104     except:  logger.log_exc("codemux.writeConf failed to restart service")
105
106
107 def sortDomains(slivers):
108     '''Given a dict of {slice: {domainname, port}}, return array of slivers with lower order domains first'''
109     dnames = {} # {host: slice}
110     for (slice, params) in slivers.iteritems():
111         for mapping in params:
112             dnames[mapping['host']] = {"slice":slice, "port": mapping['port'], "ip": mapping['ip']}
113     hosts = dnames.keys()
114     # sort by length
115     hosts.sort(key=str.__len__)
116     # longer first
117     hosts.reverse()
118     # make list of slivers
119     sortedslices = []
120     for host in hosts: sortedslices.append({host: dnames[host]})
121     
122     return sortedslices
123
124         
125 def parseConf(conf = CODEMUXCONF):
126     '''Parse the CODEMUXCONF and return dict of slices in conf. {slice: (host,port)}'''
127     slicesinconf = {} # default
128     try: 
129         f = open(conf)
130         for line in f.readlines():
131             if line.startswith("#") \
132             or (len(line.split()) > 4) \
133             or (len(line.split()) < 3):
134                 continue
135             (host, slice, port) = line.split()[:3]
136             logger.log("codemux:  found %s in conf" % slice, 2)
137             slicesinconf.setdefault(slice, [])
138             slicesinconf[slice].append({"host": host, "port": port})
139         f.close()
140     except IOError: logger.log_exc("codemux.parseConf got IOError")
141     return slicesinconf
142
143
144 def isRunning():
145     if len(os.popen("pidof codemux").readline().rstrip("\n")) > 0:
146         return True
147     else:
148         return False
149
150 def restartService():
151     if not os.path.exists("/etc/init.d/codemux"): return
152     logger.log("codemux:  Restarting codemux service")
153     if isRunning():
154         logger.log_call(["/etc/init.d/codemux","condrestart", ])
155     else:
156         logger.log_call(["/etc/init.d/codemux","restart", ])
157
158 def startService():
159     if not os.path.exists("/etc/init.d/codemux"): return
160     if not isRunning():
161         logger.log("codemux:  Starting codemux service")
162         logger.log_call(["/etc/init.d/codemux", "start", ])
163     logger.log_call(["/sbin/chkconfig", "codemux", "on"])
164
165
166 def stopService():
167     if not os.path.exists("/etc/init.d/codemux"): return
168     if isRunning():
169         logger.log("codemux:  Stopping codemux service")
170         logger.log_call(["/etc/init.d/codemux", "stop", ])
171     logger.log_call(["/sbin/chkconfig", "codemux", "off"])