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
28 # Try to load the json (2.5 and higer) or
29 # the simplejson if failed (python2.4)
33 # For old Python version, load
34 # simple json (it can be hard json?! It's 2 functions guy!)
36 import simplejson
as json
38 print "Error : you need the json or simplejson module for this script"
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"
49 'type' : 'hot_dependencies',
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
)
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
74 self
.last_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
81 self
.mapping_command_timeout
= mapping_command_timeout
84 #Called by Arbiter to say 'let's prepare yourself guy'
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
):
97 last_update
= os
.path
.getmtime(self
.mapping_file
)
98 if last_update
> self
.last_update
:
99 self
.last_update
= last_update
101 except OSError : # Maybe the file got problem, we bypaass here
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())
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
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())
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
146 if (now
- self
.last_cmd_launch
) > self
.mapping_command_timeout
:
147 print "The external command go in timeout!"
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
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
()
168 # print "The last cmd launch is too early", now - self.last_cmd_launch, self.mapping_command_interval
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
179 # If the file do not exist, we launc hthe command
181 if not self
._is
_file
_existing
():
182 self
._launch
_command
()
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)
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
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!"
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
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
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!"
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
253 extcmd
= "[%lu] DEL_HOST_DEPENDENCY;%s;%s\n" % (now
,son_name
, father_name
)
254 e
= ExternalCommand(extcmd
)
256 print 'Raising external command', extcmd