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