Fix : get back LiveStatus as default.
[shinken.git] / shinken / modules / hot_dependencies_arbiter.py
blob83e24f240d6ae773ee326a05303c557611a8f158
1 #!/usr/bin/python
2 #Copyright (C) 2009 Gabes Jean, naparuba@gmail.com
4 #This file is part of Shinken.
6 #Shinken is free software: you can redistribute it and/or modify
7 #it under the terms of the GNU Affero General Public License as published by
8 #the Free Software Foundation, either version 3 of the License, or
9 #(at your option) any later version.
11 #Shinken is distributed in the hope that it will be useful,
12 #but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 #GNU Affero General Public License for more details.
16 #You should have received a copy of the GNU Affero General Public License
17 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
20 #This Class is a plugin for the Shinken Arbiter. It read a json file
21 # with all links between objects. Update them (create/delete) at the
22 # launch or at fly
25 import time
26 import os
27 import subprocess
28 # Try to load the json (2.5 and higer) or
29 # the simplejson if failed (python2.4)
30 try:
31 import json
32 except ImportError:
33 # For old Python version, load
34 # simple json (it can be hard json?! It's 2 functions guy!)
35 try:
36 import simplejson as json
37 except ImportError:
38 print "Error : you need the json or simplejson module for this script"
39 raise
41 from shinken.basemodule import BaseModule
42 from shinken.external_command import ExternalCommand
44 #This text is print at the import
45 print "Detected module : Hot dependencies modules for Arbiter"
48 properties = {
49 'type' : 'hot_dependencies',
50 'external' : False,
51 'phases' : ['late_configuration'],
55 #called by the plugin manager to get a broker
56 def get_instance(plugin):
57 print "Get a Hot dependencies module for arbiter with plugin %s" % plugin.get_name()
58 mapping_file = getattr(plugin, 'mapping_file', '')
59 mapping_command = getattr(plugin, 'mapping_command', '')
60 mapping_command_interval = int(getattr(plugin, 'mapping_command_interval', '60'))
61 mapping_command_timeout = int(getattr(plugin, 'mapping_command_timeout', '300'))
62 instance = Hot_dependencies_arbiter(plugin, mapping_file, mapping_command, mapping_command_interval, mapping_command_timeout)
63 return instance
67 # Get hosts and/or services dep by launching a command
68 # or read a flat file as json format taht got theses links
69 class Hot_dependencies_arbiter(BaseModule):
70 def __init__(self, modconf, mapping_file, mapping_command, mapping_command_interval, mapping_command_timeout):
71 BaseModule.__init__(self, modconf)
72 self.mapping_file = mapping_file
73 self.last_update = 0
74 self.last_mapping = set()
75 self.mapping = set()
76 # The external process part
77 self.mapping_command = mapping_command
78 self.mapping_command_interval = mapping_command_interval
79 self.last_cmd_launch = 0
80 self.process = None
81 self.mapping_command_timeout = mapping_command_timeout
84 #Called by Arbiter to say 'let's prepare yourself guy'
85 def init(self):
86 print "I open the HOT dependency module"
87 # Remember what we add
90 def _is_file_existing(self):
91 return os.path.exists(self.mapping_file)
94 # Look is the mapping filechanged since the last lookup
95 def _is_mapping_file_changed(self):
96 try:
97 last_update = os.path.getmtime(self.mapping_file)
98 if last_update > self.last_update:
99 self.last_update = last_update
100 return True
101 except OSError : # Maybe the file got problem, we bypaass here
102 pass
103 return False
106 # Read the mapping file and update our internal mappings
107 def _update_mapping(self):
108 f = open(self.mapping_file, 'rb')
109 mapping = json.loads(f.read())
110 f.close()
111 self.last_mapping = self.mapping
112 # mapping is a list of list, we want a set of tuples
113 # because list cannot be hased for a set pass
114 self.mapping = set()
115 for e in mapping:
116 son, father = e
117 self.mapping.add( (tuple(son), tuple(father)) )
120 # Maybe the file is updated, but the mapping is the same
121 # if not, look at addition and remove objects
122 def _got_mapping_changes(self):
123 additions = self.mapping - self.last_mapping
124 removed = self.last_mapping - self.mapping
126 return additions, removed
128 # Launch the external command to generate the file
129 def _launch_command(self):
130 print "Launching command", self.mapping_command
131 self.last_cmd_launch = int(time.time())
132 try:
133 self.process = subprocess.Popen(
134 self.mapping_command,
135 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
136 close_fds=True, shell=True)
137 except OSError , exp:
138 print "Error in launchign the command %s : %s" % (self.mapping_command, exp)
141 # Look if the command is finished or not
142 def _watch_command_finished(self):
143 if self.process.poll() is None:
144 # Ok, still unfinished, look if in timeout
145 now = time.time()
146 if (now - self.last_cmd_launch) > self.mapping_command_timeout:
147 print "The external command go in timeout!"
148 else:
149 print "The external command is done!"
150 # it's finished! Cool
151 (stdoutdata, stderrdata) = self.process.communicate()
152 if self.process.returncode != 0:
153 print "The command return in error : %s" % stderrdata
154 print stdoutdata
155 self.process = None
158 def tick_external_command(self):
159 now = int(time.time())
161 # If we got no in progress command, look if we should launch a new one
162 if self.process is None:
163 if now - self.last_cmd_launch > self.mapping_command_interval:
164 if self.mapping_command_interval != 0 and self.mapping_command != '':
165 print "The command lunach is too old, launch a new one"
166 self._launch_command()
167 # else:
168 # print "The last cmd launch is too early", now - self.last_cmd_launch, self.mapping_command_interval
169 else:
170 # We got one in progress, we should look if it's finished or not
171 self._watch_command_finished()
173 #Ok, main function that will load dep from a json file
174 def hook_late_configuration(self, arb):
175 # We will return external commands to the arbiter, so
176 # it can jsut manage it easily and in a generic way
177 ext_cmds = []
179 # If the file do not exist, we launc hthe command
180 # and we bail out
181 if not self._is_file_existing():
182 self._launch_command()
183 return
185 self._is_mapping_file_changed()
186 self._update_mapping()
187 additions, removed = self._got_mapping_changes()
189 for (father_k, son_k) in additions:
190 son_type, son_name = son_k
191 father_type, father_name = father_k
192 print son_name, father_name
193 if son_type == 'host' and father_type == 'host':
194 son = arb.conf.hosts.find_by_name(son_name)
195 father = arb.conf.hosts.find_by_name(father_name)
196 if son is not None and father is not None:
197 print "finded!", son_name, father_name
198 if not son.is_linked_with_host(father):
199 print "Doing simple link between", son.get_name(), 'and', father.get_name()
200 # Add a dep link between the son and the father
201 son.add_host_act_dependancy(father, ['w', 'u', 'd'], None, True)
202 else:
203 print "Missing one of", son_name, father_name
207 def hook_tick(self, arb):
208 now = int(time.time())
209 self.tick_external_command()
210 print "*"*10, "Tick tick for hot dependency"
211 # If the mapping file changed, we reload it and update our links
212 # if we need it
213 if self._is_mapping_file_changed():
214 print "The mapping file changed, I update it"
215 self._update_mapping()
216 additions, removed = self._got_mapping_changes()
217 print "Additions : ", additions
218 print "Remove : ", removed
219 for father_k, son_k in additions:
220 son_type, son_name = son_k
221 father_type, father_name = father_k
222 print "Got new add", son_type, son_name, father_type, father_name
223 son = arb.conf.hosts.find_by_name(son_name.strip())
224 father = arb.conf.hosts.find_by_name(father_name.strip())
225 # if we cannot find them in the conf, bypass them
226 if son is None or father is None:
227 print "not find dumbass!"
228 continue
229 print son_name, father_name
230 if son_type == 'host' and father_type == 'host':
231 # We just raise the external command, arbiter will do the job
232 # to dispatch them
233 extcmd = "[%lu] ADD_SIMPLE_HOST_DEPENDENCY;%s;%s\n" % (now,son_name, father_name)
234 e = ExternalCommand(extcmd)
236 print 'Raising external command', extcmd
237 arb.add(e)
238 # And now the deletion part
239 for father_k, son_k in removed:
240 son_type, son_name = son_k
241 father_type, father_name = father_k
242 print "Got new del", son_type, son_name, father_type, father_name
243 son = arb.conf.hosts.find_by_name(son_name.strip())
244 father = arb.conf.hosts.find_by_name(father_name.strip())
245 # if we cannot find them in the conf, bypass them
246 if son is None or father is None:
247 print "not find dumbass!"
248 continue
249 print son_name, father_name
250 if son_type == 'host' and father_type == 'host':
251 # We just raise the external command, arbiter will do the job
252 # to dispatch them
253 extcmd = "[%lu] DEL_HOST_DEPENDENCY;%s;%s\n" % (now,son_name, father_name)
254 e = ExternalCommand(extcmd)
256 print 'Raising external command', extcmd
257 arb.add(e)
260 print '\n'*10