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