Merge branch 'master' of ssh://lausser,shinken@shinken.git.sourceforge.net/gitroot...
[shinken.git] / shinken / basemodule.py
blob012e89656b8be01861fee42fe5599af29aca9893
1 # -*- coding: utf-8 -*-
2 #Copyright (C) 2009 Gabes Jean, naparuba@gmail.com
4 #This file is part of Shinken.
6 #Shinken is free software: you can redistribute it and/or modify
7 #it under the terms of the GNU Affero General Public License as published by
8 #the Free Software Foundation, either version 3 of the License, or
9 #(at your option) any later version.
11 #Shinken is distributed in the hope that it will be useful,
12 #but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 #GNU Affero General Public License for more details.
16 #You should have received a copy of the GNU Affero General Public License
17 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
20 """ This python module contains the class BaseModule that shinken modules will subclass """
23 import os
24 import signal
25 from multiprocessing import Queue, Process
29 ## TODO: use a class for defining the module "properties" instead of plain dict ?? Like:
30 """
31 class ModuleProperties(object):
32 def __init__(self, type, phases, external=False)
33 self.type = type
34 self.phases = phases
35 self.external = external
36 """
37 ## and have the new modules instanciate this like follow :
38 """
39 properties = ModuleProperties('the_module_type', the_module_phases, is_mod_ext)
40 """
41 ##
42 """ The `properties´ dict defines what the module can do and if it's an external module or not. """
43 properties = {
44 # name of the module type ; to distinguish between them:
45 'type': None,
47 # is the module "external" (external means here a daemon module) ?
48 'external': True,
50 # Possible configuration phases where the module is involved :
51 'phases' : [ 'configuration', 'late_configuration', 'running', 'retention' ],
55 class ModulePhases:
56 ## TODO: why not use simply integers instead of string to represent the different phases ??
57 CONFIGURATION = 1
58 LATE_CONFIGURATION = 2
59 RUNNING = 4
60 RETENTION = 8
64 class BaseModule(object):
65 """ This is the base class for the shinken modules.
66 Modules can be used by the different shinken daemons/services for different tasks.
67 Example of task that a shinken module can do:
68 - load additional configuration objects.
69 - recurrently save hosts/services status/perfdata informations in different format.
70 - ...
71 """
73 def __init__(self, mod_conf):
74 """ Instanciate a new module. There can be many instance of the same type.
75 `mod_conf´ is module configuration object for this new module instance. """
76 self.myconf = mod_conf
77 self.name = mod_conf.get_name()
78 self.props = mod_conf.properties.copy()
79 self.properties = self.props # TODO: choose between 'props' or 'properties'..
80 self.interrupted = False
81 self.is_external = self.props.get('external', False)
82 self.phases = self.props.get('phases', []) # though a module defined with no phase is quite useless ..
83 self.phases.append(None)
84 self.to_q = None # the queue the module will receive data to manage
85 self.from_q = None # the queue the module will put its result data
86 self.process = None
89 def init(self):
90 """ Handle this module "post" init ; just before it'll be started.
91 Like just open necessaries file(s), database(s), or whatever the module will need. """
92 pass
94 def create_queues(self):
95 """ Create the shared queues that will be used by shinken daemon process and this module process.
96 But clear queues if they were already set before recreating new one. """
97 self.clear_queues()
98 self.from_q = Queue()
99 self.to_q = Queue()
101 def clear_queues(self):
102 """ Release the resources associated with the queues of this instance """
103 for q in (self.to_q, self.from_q):
104 if q is None: continue
105 q.close()
106 q.join_thread()
107 self.to_q = self.from_q = None
109 def start(self):
110 """ Start this module process if it's external. if not -> donothing """
111 if not self.is_external:
112 return
113 self.stop_process()
114 print("Starting external process for instance %s" % (self.name))
115 p = self.process = Process(target=self.main, args=())
116 self.properties['process'] = p ## TODO: temporary
117 p.start()
118 print("%s is now started ; pid=%d" % (self.name, p.pid))
120 def stop_process(self):
121 """ Request the module process to stop and release it """
122 if self.process:
123 self.process.terminate()
124 self.process.join(timeout=1)
125 self.process = None
128 ## TODO: are these 2 methods really needed ?
129 def get_name(self):
130 return self.name
131 def has(self, prop):
132 """ The classic has : do we have a prop or not? """
133 return hasattr(self, prop)
135 # def get_objects(self):
136 # """ Called during arbiter configuration phase. Return a dict to the objects that the module provides.
137 ##Possible objects are Host, Service, Timeperiod, etc ..
138 #Examples of valid return:
139 # h1 = { 'host_name': "server1", register=0 }
140 # return { 'hosts': [ h1 ] } """
141 # raise NotImplementedError()
144 # def update_retention_objects(self, sched, log_mgr):
145 # """ Update the retention objects of this module.
146 #Called recurrently by scheduler. Also during stop of scheduler. """
147 # raise NotImplementedError()
150 # def hook_late_configuration(self, conf):
151 # """ Hook for the module "late configuration" : Called by arbiter after the configuration has been fully loaded & built """
152 # raise NotImplementedError()
155 def manage_brok(self, brok):
156 """ Request the module to manage the given brok.
157 There a lot of different possible broks to manage. """
158 manage = getattr(self, 'manage_' + brok.type + '_brok', None)
159 if manage:
160 return manage(brok)
163 def manage_signal(self, sig, frame):
164 self.interrupted = True
166 def set_signal_handler(self, sigs=None):
167 if sigs is None:
168 sigs = (signal.SIGINT, signal.SIGTERM)
170 for sig in sigs:
171 signal.signal(sig, self.manage_signal)
173 set_exit_handler = set_signal_handler
175 def do_stop(self):
176 """ Called just before the module will exit
177 Put in this method all you need to cleanly release all open resource used by your module """
178 pass
180 def do_loop_turn(self):
181 """ For external modules only: implement in this method the body of you main loop """
182 raise NotImplementedError()
184 def main(self):
185 """ module "main" method. Only used by external modules. """
186 self.set_signal_handler()
187 print("[%s[%d]]: Now running.." % (self.name, os.getpid()))
188 while not self.interrupted:
189 self.do_loop_turn()
190 self.do_stop()
191 print("[%s]: exiting now.." % (self.name))
193 work = main # TODO: apparently some modules would uses "work" as the main method ??