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
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
= []
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 """
53 self
.get_instances(start_external
)
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
:
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
82 for module
in self
.imported_modules
:
83 if module
.properties
['type'] == module_type
:
84 self
.modules_assoc
.append((mod_conf
, module
))
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. """
96 # We setup the inst queues before its 'init' method is called. So that it can eventually get ref to the queues.
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()))
105 def clear_instances(self
, insts
=None):
106 """ Request to "remove" the given instances list or all if not provided """
108 insts
= self
.instances
[:] # have to make a copy of the list
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
:
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()))
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"
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" ?
141 self
.clear_instances(to_del
)
143 # We only start the external instances once they all have been "init" called
145 self
.__start
_ext
_instances
()
147 return self
.instances
149 def __start_ext_instances(self
):
150 for inst
in self
.instances
:
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
):
157 for inst
in self
.instances
:
158 if not self
.try_instance_init(inst
):
160 print("module instance %s could not be init")
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)
174 # Then do not listen anymore about it
175 self
.instances
.remove(inst
)
178 def check_alive_instances(self
):
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()
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
]
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
):
211 self
.clear_instances(self
.get_external_instances())