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 with .py
59 modules_files
= [ fname
[:-3] for fname
in os
.listdir(self
.modules_path
)
60 if fname
.endswith(".py") ]
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
73 del self
.imported_modules
[:]
74 for fname
in modules_files
:
75 print "Try to load", fname
78 if not hasattr(m
, 'properties'):
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
91 for module
in self
.imported_modules
:
92 if module
.properties
['type'] == module_type
:
93 self
.modules_assoc
.append((mod_conf
, module
))
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. """
105 # We setup the inst queues before its 'init' method is called. So that it can eventually get ref to the queues.
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()))
114 def clear_instances(self
, insts
=None):
115 """ Request to "remove" the given instances list or all if not provided """
117 insts
= self
.instances
[:] # have to make a copy of the list
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
:
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()))
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"
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" ?
150 self
.clear_instances(to_del
)
152 # We only start the external instances once they all have been "init" called
154 self
.__start
_ext
_instances
()
156 return self
.instances
158 def __start_ext_instances(self
):
159 for inst
in self
.instances
:
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
):
166 for inst
in self
.instances
:
167 if not self
.try_instance_init(inst
):
169 print("module instance %s could not be init")
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)
183 # Then do not listen anymore about it
184 self
.instances
.remove(inst
)
187 def check_alive_instances(self
):
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()
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
]
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
):
220 self
.clear_instances(self
.get_external_instances())