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