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 """
25 from multiprocessing
import Queue
, Process
29 ## TODO: use a class for defining the module "properties" instead of plain dict ?? Like:
31 class ModuleProperties(object):
32 def __init__(self, type, phases, external=False)
35 self.external = external
37 ## and have the new modules instanciate this like follow :
39 properties = ModuleProperties('the_module_type', the_module_phases, is_mod_ext)
42 """ The `properties´ dict defines what the module can do and if it's an external module or not. """
44 # name of the module type ; to distinguish between them:
47 # is the module "external" (external means here a daemon module) ?
50 # Possible configuration phases where the module is involved :
51 'phases' : [ 'configuration', 'late_configuration', 'running', 'retention' ],
56 ## TODO: why not use simply integers instead of string to represent the different phases ??
58 LATE_CONFIGURATION
= 2
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.
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
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. """
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. """
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
107 self
.to_q
= self
.from_q
= None
110 """ Start this module process if it's external. if not -> donothing """
111 if not self
.is_external
:
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
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 """
123 self
.process
.terminate()
124 self
.process
.join(timeout
=1)
128 ## TODO: are these 2 methods really needed ?
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)
163 def manage_signal(self
, sig
, frame
):
164 self
.interrupted
= True
166 def set_signal_handler(self
, sigs
=None):
168 sigs
= (signal
.SIGINT
, signal
.SIGTERM
)
171 signal
.signal(sig
, self
.manage_signal
)
173 set_exit_handler
= set_signal_handler
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 """
180 def do_loop_turn(self
):
181 """ For external modules only: implement in this method the body of you main loop """
182 raise NotImplementedError()
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
:
191 print("[%s]: exiting now.." % (self
.name
))
193 work
= main
# TODO: apparently some modules would uses "work" as the main method ??