*Add forgotten test case for host_without_cmd
[shinken.git] / shinken / satellitelink.py
blob9ea34038bf85a0c637a3f5d24a8600a858825a5f
1 #!/usr/bin/env python
2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
6 #This file is part of Shinken.
8 #Shinken is free software: you can redistribute it and/or modify
9 #it under the terms of the GNU Affero General Public License as published by
10 #the Free Software Foundation, either version 3 of the License, or
11 #(at your option) any later version.
13 #Shinken is distributed in the hope that it will be useful,
14 #but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 #GNU Affero General Public License for more details.
18 #You should have received a copy of the GNU Affero General Public License
19 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
22 #SatelliteLink is a common Class for link to satellite for
23 #Arbiter with Conf Dispatcher.
26 import shinken.pyro_wrapper
27 Pyro = shinken.pyro_wrapper.Pyro
30 from shinken.item import Item, Items
32 class SatelliteLink(Item):
33 #id = 0 each Class will have it's own id
34 #properties={'name' : {'required' : True },#, 'pythonize': None},
35 # 'address' : {'required' : True},#, 'pythonize': to_bool},
36 # 'port' : {'required': True, 'pythonize': to_int},
37 # 'spare' : {'required': False, 'default' : '0', 'pythonize': to_bool},
38 # }
40 #running_properties = {
41 # 'con' : {'default' : None}
42 # }
43 #macros = {}
46 #Clean? Really?
47 def clean(self):
48 pass
51 #Check is required prop are set:
52 #contacts OR contactgroups is need
53 def is_correct(self):
54 state = True #guilty or not? :)
55 cls = self.__class__
57 special_properties = ['realm']
58 for prop in cls.properties:
59 if prop not in special_properties:
60 if not hasattr(self, prop) and cls.properties[prop]['required']:
61 print self.get_name(), " : I do not have", prop
62 state = False #Bad boy...
63 #Ok now we manage special cases...
64 if not hasattr(self, 'realm') or hasattr(self, 'realm') and self.realm == None:
65 print self.get_name()," : I do not have a valid realm"
66 state = False
67 return state
70 def create_connexion(self):
71 #URI are differents between 3 and 4
72 if shinken.pyro_wrapper.pyro_version == 3:
73 self.uri = 'PYROLOC://'+self.address+":"+str(self.port)+"/ForArbiter"
74 self.con = Pyro.core.getProxyForURI(self.uri)
75 #Ok, set timeout to 3 sec (ping timeout)
76 self.con._setTimeout(self.timeout)
77 else:
78 self.uri = 'PYRO:ForArbiter@'+self.address+":"+str(self.port)
79 self.con = Pyro.core.Proxy(self.uri)
80 self.con._pyroTimeout = self.timeout
83 def put_conf(self, conf):
84 if self.con == None:
85 self.create_connexion()
86 #print "Connexion is OK, now we put conf", conf
87 #print "Try to put conf:", conf
88 try:
89 #Still fun with pyro 3 and 4...
90 if shinken.pyro_wrapper.pyro_version == 3:
91 #Data timeout is far longer than timeout (ping one)
92 self.con._setTimeout(self.data_timeout)
93 self.con.put_conf(conf)
94 self.con._setTimeout(self.timeout)
95 return True
96 else:
97 self.con._pyroTimeout = self.data_timeout
98 self.con.put_conf(conf)
99 self.con._pyroTimeout = self.timeout
100 return True
101 except Pyro.errors.URIError , exp:
102 self.con = None
103 return False
104 except Pyro.errors.ProtocolError , exp:
105 self.con = None
106 return False
107 except TypeError , exp:
108 print ''.join(Pyro.util.getPyroTraceback(exp))
109 except Pyro.errors.CommunicationError , exp:
110 self.con = None
111 return False
114 #Get and clean all of our broks
115 def get_all_broks(self):
116 res = self.broks
117 self.broks = []
118 return res
121 #Set alive, reachable, and reset attemps.
122 #If we change state, raise a status brok update
123 def set_alive(self):
124 was_alive = self.alive
125 self.alive = True
126 self.attempt = 0
127 self.reachable = True
129 #We came from dead to alive
130 #so we must add a brok update
131 if not was_alive:
132 b = self.get_update_status_brok()
133 self.broks.append(b)
136 def set_dead(self):
137 print "Set dead for %s" % self.get_name()
138 was_alive = self.alive
139 self.alive = False
140 self.con = None
142 #We are dead now. Must raise
143 #a brok to say it
144 if was_alive:
145 b = self.get_update_status_brok()
146 self.broks.append(b)
149 #Go in reachable=False and add a failed attempt
150 #if we reach the max, go dead
151 def add_failed_check_attempt(self):
152 print "Add failed attempt to", self.get_name()
153 self.reachable = False
154 self.attempt += 1
155 self.attempt = min(self.attempt, self.max_check_attempts)
156 print "Attemps", self.attempt, self.max_check_attempts
157 #check when we just go HARD (dead)
158 if self.attempt == self.max_check_attempts:
159 self.set_dead()
162 def ping(self):
163 print "Pinging %s" % self.get_name()
164 try:
165 if self.con == None:
166 self.create_connexion()
167 self.con.ping()
168 self.set_alive()
169 except Pyro.errors.ProtocolError , exp:
170 self.add_failed_check_attempt()
171 except Pyro.errors.URIError , exp:
172 print exp
173 self.add_failed_check_attempt()
174 #Only pyro 4 but will be ProtocolError in 3
175 except Pyro.errors.CommunicationError , exp:
176 #print "Is not alive!", self.uri
177 self.add_failed_check_attempt()
178 except Pyro.errors.DaemonError , exp:
179 print exp
180 self.add_failed_check_attempt()
183 def wait_new_conf(self):
184 if self.con == None:
185 self.create_connexion()
186 try:
187 self.con.wait_new_conf()
188 return True
189 except Pyro.errors.URIError , exp:
190 self.con = None
191 return False
192 except Pyro.errors.ProtocolError , exp:
193 self.con = None
194 return False
197 #To know if the satellite have a conf (magic_hash = None)
198 #OR to know if the satellite have THIS conf (magic_hash != None)
199 def have_conf(self, magic_hash=None):
200 if self.con == None:
201 self.create_connexion()
203 try:
204 if magic_hash == None:
205 return self.con.have_conf()
206 else:
207 return self.con.have_conf(magic_hash)
208 except Pyro.errors.URIError , exp:
209 self.con = None
210 return False
211 except Pyro.errors.ProtocolError , exp:
212 self.con = None
213 return False
216 def remove_from_conf(self, sched_id):
217 if self.con == None:
218 self.create_connexion()
219 try:
220 self.con.remove_from_conf(sched_id)
221 return True
222 except Pyro.errors.URIError , exp:
223 self.con = None
224 return False
225 except Pyro.errors.ProtocolError , exp:
226 self.con = None
227 return False
230 def what_i_managed(self):
231 if self.con == None:
232 self.create_connexion()
233 try:
234 tab = self.con.what_i_managed()
235 #I don't know why, but tab can be a bool. Not good here
236 if isinstance(tab, bool):
237 self.con = None
238 return []
239 return tab
240 except Pyro.errors.URIError , exp:
241 self.con = None
242 return []
243 except Pyro.errors.ProtocolError , exp:
244 self.con = None
245 return []
248 def push_broks(self, broks):
249 if self.con == None:
250 self.create_connexion()
251 try:
252 return self.con.push_broks(broks)
253 except Pyro.errors.URIError , exp:
254 self.con = None
255 return False
256 except Pyro.errors.ProtocolError , exp:
257 self.con = None
258 return False
259 except AttributeError , exp:
260 print exp
261 return False
264 def get_external_commands(self):
265 if self.con == None:
266 self.create_connexion()
267 try:
268 tab = self.con.get_external_commands()
269 if isinstance(tab, bool):
270 return []
271 return tab
272 except Pyro.errors.URIError , exp:
273 self.con = None
274 return []
275 except Pyro.errors.ProtocolError , exp:
276 self.con = None
277 return []
278 except AttributeError , exp:
279 print exp
280 return []
284 def prepare_for_conf(self):
285 self.cfg = { 'global' : {}, 'schedulers' : {}, 'arbiters' : {}}
286 #cfg_for_satellite['modules'] = satellite.modules
287 properties = self.__class__.properties
288 for prop in properties:
289 # if 'to_send' in properties[prop] and properties[prop]['to_send']:
290 if properties[prop].to_send:
291 self.cfg['global'][prop] = getattr(self, prop)
293 #Some parameters for satellites are not defined in the satellites conf
294 #but in the global configuration. We can pass them in the global
295 #property
296 def add_global_conf_parameters(self, params):
297 for prop in params:
298 print "Add global parameter", prop, params[prop]
299 self.cfg['global'][prop] = params[prop]
302 def get_my_type(self):
303 return self.__class__.my_type
306 #Here for poller and reactionner. Scheduler have it's own function
307 def give_satellite_cfg(self):
308 return {'port' : self.port, 'address' : self.address, 'name' : self.get_name(), 'instance_id' : self.id, 'active' : True}
312 class SatelliteLinks(Items):
313 #name_property = "name"
314 #inner_class = SchedulerLink
316 #We must have a realm property, so we find our realm
317 def linkify(self, realms, modules):
318 self.linkify_s_by_p(realms)
319 self.linkify_s_by_plug(modules)
322 def linkify_s_by_p(self, realms):
323 for s in self:
324 p_name = s.realm.strip()
325 # If no realm name, take the default one
326 if p_name == '':
327 p = realms.get_default()
328 s.realm = p
329 else: # find the realm one
330 p = realms.find_by_name(p_name)
331 s.realm = p
332 # Check if what we get is OK or not
333 if p is not None:
334 print "Me", s.get_name(), "is linked with realm", s.realm.get_name()
335 s.register_to_my_realm()
336 else:
337 err = "The %s %s got a unknown realm '%s'" % (s.__class__.my_type, s.get_name(), p_name)
338 s.configuration_errors.append(err)
339 print err
342 def linkify_s_by_plug(self, modules):
343 for s in self:
344 new_modules = []
345 for plug_name in s.modules:
346 plug = modules.find_by_name(plug_name.strip())
347 if plug != None:
348 new_modules.append(plug)
349 else:
350 print "Error : the module %s is unknow for %s" % (plug_name, s.get_name())
351 s.modules = new_modules