/usr/lib/python2.7/dist-packages/shinken/modulesmanager.py is in shinken-common 2.0.3-4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | #!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2009-2012:
# Gabes Jean, naparuba@gmail.com
# Gerhard Lausser, Gerhard.Lausser@consol.de
# Gregory Starck, g.starck@gmail.com
# Hartmut Goebel, h.goebel@goebel-consult.de
#
# This file is part of Shinken.
#
# Shinken is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Shinken is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken. If not, see <http://www.gnu.org/licenses/>.
import os
import time
import sys
import traceback
import cStringIO
import imp
from shinken.basemodule import BaseModule
from shinken.log import logger
# We need to manage pre-2.0 module types with _ into the new 2.0 - mode
def uniform_module_type(s):
return s.replace('_', '-')
class ModulesManager(object):
"""This class is use to manage modules and call callback"""
def __init__(self, modules_type, modules_path, modules):
self.modules_path = modules_path
self.modules_type = modules_type
self.modules = modules
self.allowed_types = [uniform_module_type(plug.module_type) for plug in modules]
self.imported_modules = []
self.modules_assoc = []
self.instances = []
self.to_restart = []
self.max_queue_size = 0
self.manager = None
def load_manager(self, manager):
self.manager = manager
# Set the modules requested for this manager
def set_modules(self, modules):
self.modules = modules
self.allowed_types = [uniform_module_type(mod.module_type) for mod in modules]
def set_max_queue_size(self, max_queue_size):
self.max_queue_size = max_queue_size
# Import, instanciate & "init" the modules we have been requested
def load_and_init(self):
self.load()
self.get_instances()
# Try to import the requested modules ; put the imported modules in self.imported_modules.
# The previous imported modules, if any, are cleaned before.
def load(self):
now = int(time.time())
# We get all modules file with .py
modules_files = []#fname[:-3] for fname in os.listdir(self.modules_path)
#if fname.endswith(".py")]
# And directories
modules_files.extend([fname for fname in os.listdir(self.modules_path)
if os.path.isdir(os.path.join(self.modules_path, fname))])
# Now we try to load them
# So first we add their dir into the sys.path
if not self.modules_path in sys.path:
sys.path.append(self.modules_path)
# We try to import them, but we keep only the one of
# our type
del self.imported_modules[:]
for fname in modules_files:
try:
# Then we load the module.py inside this directory
mod_file = os.path.abspath(os.path.join(self.modules_path, fname,'module.py'))
mod_dir = os.path.dirname(mod_file)
# We add this dir to sys.path so the module can load local files too
sys.path.append(mod_dir)
if not os.path.exists(mod_file):
mod_file = os.path.abspath(os.path.join(self.modules_path, fname,'module.pyc'))
m = None
if mod_file.endswith('.py'):
# important, equivalent to import fname from module.py
m = imp.load_source(fname, mod_file)
else:
m = imp.load_compiled(fname, mod_file)
m_dir = os.path.abspath(os.path.dirname(m.__file__))
# Look if it's a valid module
if not hasattr(m, 'properties'):
logger.warning('Bad module file for %s : missing properties dict' % mod_file)
continue
# We want to keep only the modules of our type
if self.modules_type in m.properties['daemons']:
self.imported_modules.append(m)
except Exception, exp:
# Oups, somethign went wrong here...
logger.warning("Importing module %s: %s" % (fname, exp))
# Now we want to find in theses modules the ones we are looking for
del self.modules_assoc[:]
for mod_conf in self.modules:
module_type = uniform_module_type(mod_conf.module_type)
is_find = False
for module in self.imported_modules:
if uniform_module_type(module.properties['type']) == module_type:
self.modules_assoc.append((mod_conf, module))
is_find = True
break
if not is_find:
# No module is suitable, we Raise a Warning
logger.warning("The module type %s for %s was not found in modules!" % (module_type, mod_conf.get_name()))
# Try to "init" the given module instance.
# If late_start, don't look for last_init_try
# Returns: True on successful init. False if instance init method raised any Exception.
def try_instance_init(self, inst, late_start=False):
try:
logger.info("Trying to init module: %s" % inst.get_name())
inst.init_try += 1
# Maybe it's a retry
if not late_start and inst.init_try > 1:
# Do not try until 5 sec, or it's too loopy
if inst.last_init_try > time.time() - 5:
return False
inst.last_init_try = time.time()
# If it's an external, create/update Queues()
if inst.is_external:
inst.create_queues(self.manager)
inst.init()
except Exception, e:
logger.error("The instance %s raised an exception %s, I remove it!" % (inst.get_name(), str(e)))
output = cStringIO.StringIO()
traceback.print_exc(file=output)
logger.error("Back trace of this remove: %s" % (output.getvalue()))
output.close()
return False
return True
# Request to "remove" the given instances list or all if not provided
def clear_instances(self, insts=None):
if insts is None:
insts = self.instances[:] # have to make a copy of the list
for i in insts:
self.remove_instance(i)
# Put an instance to the restart queue
def set_to_restart(self, inst):
self.to_restart.append(inst)
# actually only arbiter call this method with start_external=False..
# Create, init and then returns the list of module instances that the caller needs.
# If an instance can't be created or init'ed then only log is done.
# That instance is skipped. The previous modules instance(s), if any, are all cleaned.
def get_instances(self):
self.clear_instances()
for (mod_conf, module) in self.modules_assoc:
try:
mod_conf.properties = module.properties.copy()
inst = module.get_instance(mod_conf)
# Give the module the data to which module it is load from
inst.set_loaded_into(self.modules_type)
if inst is None: # None = Bad thing happened :)
logger.info("get_instance for module %s returned None!" % (mod_conf.get_name()))
continue
assert(isinstance(inst, BaseModule))
self.instances.append(inst)
except Exception, exp:
s = str(exp)
if isinstance(s, str):
s = s.decode('UTF-8', 'replace')
logger.error("The module %s raised an exception %s, I remove it!" % (mod_conf.get_name(), s))
output = cStringIO.StringIO()
traceback.print_exc(file=output)
logger.error("Back trace of this remove: %s" % (output.getvalue()))
output.close()
for inst in self.instances:
# External are not init now, but only when they are started
if not inst.is_external and not self.try_instance_init(inst):
# If the init failed, we put in in the restart queue
logger.warning("The module '%s' failed to init, I will try to restart it later" % inst.get_name())
self.to_restart.append(inst)
return self.instances
# Launch external instances that are load correctly
def start_external_instances(self, late_start=False):
for inst in [inst for inst in self.instances if inst.is_external]:
# But maybe the init failed a bit, so bypass this ones from now
if not self.try_instance_init(inst, late_start=late_start):
logger.warning("The module '%s' failed to init, I will try to restart it later" % inst.get_name())
self.to_restart.append(inst)
continue
# ok, init succeed
logger.info("Starting external module %s" % inst.get_name())
inst.start()
# Request to cleanly remove the given instance.
# If instance is external also shutdown it cleanly
def remove_instance(self, inst):
# External instances need to be close before (process + queues)
if inst.is_external:
logger.debug("Ask stop process for %s" % inst.get_name())
inst.stop_process()
logger.debug("Stop process done")
inst.clear_queues(self.manager)
# Then do not listen anymore about it
self.instances.remove(inst)
def check_alive_instances(self):
# Only for external
for inst in self.instances:
if not inst in self.to_restart:
if inst.is_external and not inst.process.is_alive():
logger.error("The external module %s goes down unexpectedly!" % inst.get_name())
logger.info("Setting the module %s to restart" % inst.get_name())
# We clean its queues, they are no more useful
inst.clear_queues(self.manager)
self.to_restart.append(inst)
# Ok, no need to look at queue size now
continue
# Now look for man queue size. If above value, the module should got a huge problem
# and so bailout. It's not a perfect solution, more a watchdog
# If max_queue_size is 0, don't check this
if self.max_queue_size == 0:
continue
# Ok, go launch the dog!
queue_size = 0
try:
queue_size = inst.to_q.qsize()
except Exception, exp:
pass
if queue_size > self.max_queue_size:
logger.error("The external module %s got a too high brok queue size (%s > %s)!" % (inst.get_name(), queue_size, self.max_queue_size))
logger.info("Setting the module %s to restart" % inst.get_name())
# We clean its queues, they are no more useful
inst.clear_queues(self.manager)
self.to_restart.append(inst)
def try_to_restart_deads(self):
to_restart = self.to_restart[:]
del self.to_restart[:]
for inst in to_restart:
logger.debug("I should try to reinit %s" % inst.get_name())
if self.try_instance_init(inst):
logger.debug("Good, I try to restart %s" % inst.get_name())
# If it's an external, it will start it
inst.start()
# Ok it's good now :)
else:
self.to_restart.append(inst)
# Do not give to others inst that got problems
def get_internal_instances(self, phase=None):
return [inst for inst in self.instances if not inst.is_external and phase in inst.phases and inst not in self.to_restart]
def get_external_instances(self, phase=None):
return [inst for inst in self.instances if inst.is_external and phase in inst.phases and inst not in self.to_restart]
def get_external_to_queues(self):
return [inst.to_q for inst in self.instances if inst.is_external and inst not in self.to_restart]
def get_external_from_queues(self):
return [inst.from_q for inst in self.instances if inst.is_external and inst not in self.to_restart]
def stop_all(self):
# Ask internal to quit if they can
for inst in self.get_internal_instances():
if hasattr(inst, 'quit') and callable(inst.quit):
inst.quit()
self.clear_instances([inst for inst in self.instances if inst.is_external])
|