Fix : get back LiveStatus as default.
[shinken.git] / shinken / modulesmanager.py
blobfa203d3e783a9571715103ee40be2819d9ec54c6
1 #!/usr/bin/env python
2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
5 # Gregory Starck, g.starck@gmail.com
6 # Hartmut Goebel, h.goebel@goebel-consult.de
8 #This file is part of Shinken.
10 #Shinken is free software: you can redistribute it and/or modify
11 #it under the terms of the GNU Affero General Public License as published by
12 #the Free Software Foundation, either version 3 of the License, or
13 #(at your option) any later version.
15 #Shinken is distributed in the hope that it will be useful,
16 #but WITHOUT ANY WARRANTY; without even the implied warranty of
17 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 #GNU Affero General Public License for more details.
20 #You should have received a copy of the GNU Affero General Public License
21 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
24 #This class is use to manage modules and call callback
26 import os
27 import sys
28 import traceback
30 from shinken.basemodule import BaseModule
33 class ModulesManager(object):
35 def __init__(self, modules_type, modules_path, modules):
36 self.modules_path = modules_path
37 self.modules_type = modules_type
38 self.modules = modules
39 self.allowed_types = [ plug.module_type for plug in modules ]
40 self.imported_modules = []
41 self.modules_assoc = []
42 self.instances = []
44 def set_modules(self, modules):
46 """ Set the modules requested for this manager """
47 self.modules = modules
48 self.allowed_types = [ mod.module_type for mod in modules ]
50 def load_and_init(self, start_external=True):
51 """ Import, instanciate & "init" the modules we have been requested """
52 self.load()
53 self.get_instances(start_external)
55 def load(self):
56 """ Try to import the requested modules ; put the imported modules in self.imported_modules.
57 The previous imported modules, if any, are cleaned before. """
58 #We get all modules file of our type (end with broker.py for example)
59 modules_files = [ fname[:-3] for fname in os.listdir(self.modules_path)
60 if fname.endswith(self.modules_type+".py") ]
62 #And directories (no remove of .py but still with broker for example at the end)
63 modules_files.extend([ fname for fname in os.listdir(self.modules_path)
64 if fname.endswith(self.modules_type) ])
66 # Now we try to load thems
67 if not self.modules_path in sys.path:
68 sys.path.append(self.modules_path)
70 del self.imported_modules[:]
71 for fname in modules_files:
72 try:
73 print("importing %s" % (fname))
74 self.imported_modules.append(__import__(fname))
75 except ImportError , exp:
76 print "Warning :", exp
78 del self.modules_assoc[:]
79 for mod_conf in self.modules:
80 module_type = mod_conf.module_type
81 is_find = False
82 for module in self.imported_modules:
83 if module.properties['type'] == module_type:
84 self.modules_assoc.append((mod_conf, module))
85 is_find = True
86 break
87 if not is_find:
88 #No module is suitable, we Raise a Warning
89 print "Warning : the module type %s for %s was not found in modules!" % (module_type, mod_conf.get_name())
92 def try_instance_init(self, inst):
93 """ Try to "init" the given module instance.
94 Returns: True on successfull init. False if instance init method raised any Exception. """
95 try:
96 # We setup the inst queues before its 'init' method is called. So that it can eventually get ref to the queues.
97 inst.create_queues()
98 inst.init()
99 except Exception, e:
100 print("Error : the instance %s raised an exception %s, I remove it!" % (inst.get_name(), str(e)))
101 print("Back trace of this remove : %s" % (traceback.format_exc()))
102 return False
103 return True
105 def clear_instances(self, insts=None):
106 """ Request to "remove" the given instances list or all if not provided """
107 if insts is None:
108 insts = self.instances[:] # have to make a copy of the list
109 for i in insts:
110 self.remove_instance(i)
112 # actually only arbiter call this method with start_external=False..
113 def get_instances(self, start_external=True):
114 """ Create, init and then returns the list of module instances that the caller needs.
115 By default (start_external=True) also start the execution of the instances that are "external".
116 If an instance can't be created or init'ed then only log is done. That instance is skipped.
117 The previous modules instance(s), if any, are all cleaned. """
118 self.clear_instances()
119 for (mod_conf, module) in self.modules_assoc:
120 try:
121 mod_conf.properties = module.properties.copy()
122 inst = module.get_instance(mod_conf)
123 if inst is None: #None = Bad thing happened :)
124 print("get_instance for module %s returned None !" % (mod_conf.get_name()))
125 continue
126 assert(isinstance(inst, BaseModule))
127 self.instances.append(inst)
128 except Exception , exp:
129 print "Error : the module %s raised an exception %s, I remove it!" % (mod_conf.get_name(), str(exp))
130 print "Back trace of this remove :"
131 traceback.print_exc(file=sys.stdout)
133 print "Load", len(self.instances), "module instances"
135 to_del = []
136 for inst in self.instances:
137 if not self.try_instance_init(inst):
138 # TODO: isn't it very bad if a requested module instance can't be "initialized" ?
139 to_del.append(inst)
141 self.clear_instances(to_del)
143 # We only start the external instances once they all have been "init" called
144 if start_external:
145 self.__start_ext_instances()
147 return self.instances
149 def __start_ext_instances(self):
150 for inst in self.instances:
151 inst.start()
153 # actually only called by arbiter... because it instanciate its modules before going daemon
154 # TODO: but this actually leads to a double "init" call.. maybe a "uninit" would be needed ?
155 def init_and_start_instances(self):
156 to_del = []
157 for inst in self.instances:
158 if not self.try_instance_init(inst):
159 # damn..
160 print("module instance %s could not be init")
161 to_del.append(inst)
162 self.clear_instances(to_del)
163 self.__start_ext_instances()
165 def remove_instance(self, inst):
166 """ Request to cleanly remove the given instance.
167 If instance is external also shutdown it cleanly """
168 # External instances need to be close before (process + queues)
169 if inst.is_external:
170 inst.stop_process()
172 inst.clear_queues()
174 # Then do not listen anymore about it
175 self.instances.remove(inst)
178 def check_alive_instances(self):
179 to_del = []
180 #Only for external
181 for inst in self.instances:
182 if inst.is_external and not inst.process.is_alive():
183 print "Error : the external module %s goes down unexpectly!" % inst.get_name()
184 to_del.append(inst)
186 self.clear_instances(to_del)
189 def get_internal_instances(self, phase=None):
190 return [ inst for inst in self.instances if not inst.is_external and phase in inst.phases ]
193 def get_external_instances(self, phase=None):
194 return [ inst for inst in self.instances if inst.is_external and phase in inst.phases ]
197 def get_external_to_queues(self, phase=None):
198 return [ inst.to_q for inst in self.instances if inst.is_external and phase in inst.phases ]
201 def get_external_from_queues(self, phase=None):
202 return [ inst.from_q for inst in self.instances if inst.is_external and phase in inst.phases ]
205 def stop_all(self):
206 #Ask internal to quit if they can
207 for inst in self.get_internal_instances():
208 if hasattr(inst, 'quit') and callable(inst.quit):
209 inst.quit()
211 self.clear_instances(self.get_external_instances())