Merge branch 'master' of ssh://lausser,shinken@shinken.git.sourceforge.net/gitroot...
[shinken.git] / shinken / modulesmanager.py
blob86ec62bf170e02d96efbd02fc74f30e1c0aa1b2e
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 with .py
59 modules_files = [ fname[:-3] for fname in os.listdir(self.modules_path)
60 if fname.endswith(".py") ]
62 # And directories
63 modules_files.extend([ fname for fname in os.listdir(self.modules_path)
64 if os.path.isdir(os.path.join(self.modules_path, fname)) ])
66 # Now we try to load thems
67 # So first we add their dir into the sys.path
68 if not self.modules_path in sys.path:
69 sys.path.append(self.modules_path)
71 # We try to import them, but we keep only the one of
72 # our type
73 del self.imported_modules[:]
74 for fname in modules_files:
75 print "Try to load", fname
76 try:
77 m = __import__(fname)
78 if not hasattr(m, 'properties'):
79 continue
81 # We want to keep only the modules of our type
82 if self.modules_type in m.properties['daemons']:
83 self.imported_modules.append(m)
84 except ImportError , exp:
85 print "Warning :", exp
87 del self.modules_assoc[:]
88 for mod_conf in self.modules:
89 module_type = mod_conf.module_type
90 is_find = False
91 for module in self.imported_modules:
92 if module.properties['type'] == module_type:
93 self.modules_assoc.append((mod_conf, module))
94 is_find = True
95 break
96 if not is_find:
97 #No module is suitable, we Raise a Warning
98 print "Warning : the module type %s for %s was not found in modules!" % (module_type, mod_conf.get_name())
101 def try_instance_init(self, inst):
102 """ Try to "init" the given module instance.
103 Returns: True on successfull init. False if instance init method raised any Exception. """
104 try:
105 # We setup the inst queues before its 'init' method is called. So that it can eventually get ref to the queues.
106 inst.create_queues()
107 inst.init()
108 except Exception, e:
109 print("Error : the instance %s raised an exception %s, I remove it!" % (inst.get_name(), str(e)))
110 print("Back trace of this remove : %s" % (traceback.format_exc()))
111 return False
112 return True
114 def clear_instances(self, insts=None):
115 """ Request to "remove" the given instances list or all if not provided """
116 if insts is None:
117 insts = self.instances[:] # have to make a copy of the list
118 for i in insts:
119 self.remove_instance(i)
121 # actually only arbiter call this method with start_external=False..
122 def get_instances(self, start_external=True):
123 """ Create, init and then returns the list of module instances that the caller needs.
124 By default (start_external=True) also start the execution of the instances that are "external".
125 If an instance can't be created or init'ed then only log is done. That instance is skipped.
126 The previous modules instance(s), if any, are all cleaned. """
127 self.clear_instances()
128 for (mod_conf, module) in self.modules_assoc:
129 try:
130 mod_conf.properties = module.properties.copy()
131 inst = module.get_instance(mod_conf)
132 if inst is None: #None = Bad thing happened :)
133 print("get_instance for module %s returned None !" % (mod_conf.get_name()))
134 continue
135 assert(isinstance(inst, BaseModule))
136 self.instances.append(inst)
137 except Exception , exp:
138 print "Error : the module %s raised an exception %s, I remove it!" % (mod_conf.get_name(), str(exp))
139 print "Back trace of this remove :"
140 traceback.print_exc(file=sys.stdout)
142 print "Load", len(self.instances), "module instances"
144 to_del = []
145 for inst in self.instances:
146 if not self.try_instance_init(inst):
147 # TODO: isn't it very bad if a requested module instance can't be "initialized" ?
148 to_del.append(inst)
150 self.clear_instances(to_del)
152 # We only start the external instances once they all have been "init" called
153 if start_external:
154 self.__start_ext_instances()
156 return self.instances
158 def __start_ext_instances(self):
159 for inst in self.instances:
160 inst.start()
162 # actually only called by arbiter... because it instanciate its modules before going daemon
163 # TODO: but this actually leads to a double "init" call.. maybe a "uninit" would be needed ?
164 def init_and_start_instances(self):
165 to_del = []
166 for inst in self.instances:
167 if not self.try_instance_init(inst):
168 # damn..
169 print("module instance %s could not be init")
170 to_del.append(inst)
171 self.clear_instances(to_del)
172 self.__start_ext_instances()
174 def remove_instance(self, inst):
175 """ Request to cleanly remove the given instance.
176 If instance is external also shutdown it cleanly """
177 # External instances need to be close before (process + queues)
178 if inst.is_external:
179 inst.stop_process()
181 inst.clear_queues()
183 # Then do not listen anymore about it
184 self.instances.remove(inst)
187 def check_alive_instances(self):
188 to_del = []
189 #Only for external
190 for inst in self.instances:
191 if inst.is_external and not inst.process.is_alive():
192 print "Error : the external module %s goes down unexpectly!" % inst.get_name()
193 to_del.append(inst)
195 self.clear_instances(to_del)
198 def get_internal_instances(self, phase=None):
199 return [ inst for inst in self.instances if not inst.is_external and phase in inst.phases ]
202 def get_external_instances(self, phase=None):
203 return [ inst for inst in self.instances if inst.is_external and phase in inst.phases ]
206 def get_external_to_queues(self, phase=None):
207 return [ inst.to_q for inst in self.instances if inst.is_external and phase in inst.phases ]
210 def get_external_from_queues(self, phase=None):
211 return [ inst.from_q for inst in self.instances if inst.is_external and phase in inst.phases ]
214 def stop_all(self):
215 #Ask internal to quit if they can
216 for inst in self.get_internal_instances():
217 if hasattr(inst, 'quit') and callable(inst.quit):
218 inst.quit()
220 self.clear_instances(self.get_external_instances())