""" 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. """ # NOTE # in November 2015 it was established that this plugin is the culprit # for our long standing slice re-creation issue # for this reason this is now turned off on lxc-based hosts # as per the spec file this plugin gets packaged as part of # nodemanager-vs and not nodemanager-lib anymore import logger import os from config import Config import slivermanager CODEMUXCONF="/etc/codemux/codemux.conf" def start(): logger.log("codemux: plugin starting up...") def GetSlivers(data, config, plc = None): """ For each sliver with the codemux attribute, parse out "host,port" and make entry in conf. Restart service after. """ if 'OVERRIDES' in dir(config): if config.OVERRIDES.get('codemux') == '-1': logger.log("codemux: Disabled", 2) stopService() return 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 "root" in slicesinconf: _writeconf = False else: _writeconf = True # Parse attributes and update dict of scripts if 'slivers' not in data: logger.log_missing_data("codemux.GetSlivers", 'slivers') return for sliver in data['slivers']: for attribute in sliver['attributes']: if attribute['tagname'] == 'codemux': # add to conf. Attribute is [host, port] parts = attribute['value'].split(",") if len(parts) < 2: logger.log("codemux: attribute value (%s) for codemux not separated by comma. Skipping." %attribute['value']) continue if len(parts) == 3: ip = parts[2] else: ip = "" params = {'host': parts[0], 'port': parts[1], 'ip': ip} try: # Check to see if sliver is running. If not, continue if slivermanager.is_running(sliver['name']): # Check if new or needs updating if (sliver['name'] not in list(slicesinconf.keys())) \ or (params not in slicesinconf.get(sliver['name'], [])): logger.log("codemux: Updating 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)) # ensure the service is running startService() 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.items(): if params['slice'] == "root": continue f.write("%s %s %s %s\n" % (host, params['slice'], params['port'], params['ip'])) f.truncate() f.close() try: restartService() except: logger.log_exc("codemux.writeConf failed to restart service") 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.items(): for mapping in params: dnames[mapping['host']] = {"slice":slice, "port": mapping['port'], "ip": mapping['ip']} hosts = list(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(): parts = line.split() if line.startswith("#") or (len(parts) > 4) or (len(parts) < 3): continue if len(parts) == 4: (host, slice, port, ip) = parts logger.log("codemux: found %s in conf" % slice, 2) slicesinconf.setdefault(slice, []) slicesinconf[slice].append({"host": host, "port": port, "ip": ip}) else: (host, slice, port) = parts[: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("codemux.parseConf got IOError") return slicesinconf def isRunning(): if len(os.popen("pidof codemux").readline().rstrip("\n")) > 0: return True else: return False def restartService(): if not os.path.exists("/etc/init.d/codemux"): return logger.log("codemux: Restarting codemux service") if isRunning(): logger.log_call(["/etc/init.d/codemux", "condrestart", ]) else: logger.log_call(["/etc/init.d/codemux", "restart", ]) def startService(): if not os.path.exists("/etc/init.d/codemux"): return if not isRunning(): logger.log("codemux: Starting codemux service") logger.log_call(["/etc/init.d/codemux", "start", ]) logger.log_call(["/sbin/chkconfig", "codemux", "on"]) def stopService(): if not os.path.exists("/etc/init.d/codemux"): return if isRunning(): logger.log("codemux: Stopping codemux service") logger.log_call(["/etc/init.d/codemux", "stop", ]) logger.log_call(["/sbin/chkconfig", "codemux", "off"])