6d6769b972947ebcc18e8d44b9c1e650ae04e32c
[collecty.git] / src / collecty / daemon.py
1 #!/usr/bin/python
2 ###############################################################################
3 #                                                                             #
4 # collecty - A system statistics collection daemon for IPFire                 #
5 # Copyright (C) 2012 IPFire development team                                  #
6 #                                                                             #
7 # This program is free software: you can redistribute it and/or modify        #
8 # it under the terms of the GNU General Public License as published by        #
9 # the Free Software Foundation, either version 3 of the License, or           #
10 # (at your option) any later version.                                         #
11 #                                                                             #
12 # This program is distributed in the hope that it will be useful,             #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of              #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
15 # GNU General Public License for more details.                                #
16 #                                                                             #
17 # You should have received a copy of the GNU General Public License           #
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
19 #                                                                             #
20 ###############################################################################
21
22 import signal
23
24 import ConfigParser as configparser
25
26 import plugins
27
28 from constants import *
29 from i18n import _
30
31 import logging
32 log = logging.getLogger("collecty")
33
34 class Collecty(object):
35         # The default interval, when all data is written to disk.
36         SUBMIT_INTERVAL = 300
37
38         def __init__(self, debug=False):
39                 self.config = configparser.ConfigParser()
40                 self.data_sources = []
41
42                 # Indicates whether this process should be running or not.
43                 self.running = True
44                 self.timer = plugins.Timer(self.SUBMIT_INTERVAL, heartbeat=2)
45
46                 # Add all automatic data sources.
47                 self.add_autocreate_data_sources()
48
49                 log.info(_("Collecty successfully initialized."))
50
51         def add_autocreate_data_sources(self):
52                 for data_source in plugins.data_sources:
53                         if not hasattr(data_source, "autocreate"):
54                                 continue
55
56                         ret = data_source.autocreate(self)
57                         if not ret:
58                                 continue
59
60                         if not type(ret) == type([]):
61                                 ret = [ret,]
62
63                         log.debug(_("Data source '%(name)s' registered %(number)s instance(s).") % \
64                                 { "name" : data_source.name, "number" : len(ret) })
65
66                         self.data_sources += ret
67
68         def read_config(self, config):
69                 self.config.read(config)
70                 
71                 for section in self.config.sections():
72                         try:
73                                 data_source = self.config.get(section, "data_source")
74                                 data_source = plugins.find(data_source)
75                         except configparser.NoOptionError:
76                                 raise ConfigError, "Syntax error in configuration: plugin option is missing."
77                         except:
78                                 raise Exception, "Plugin configuration error: Maybe plugin wasn't found? %s" % data_source
79
80                         kwargs = {}
81                         for (key, value) in self.config.items(section):
82                                 if key == "plugin":
83                                         continue
84
85                         kwargs[key] = value
86                         kwargs["file"] = section
87
88                         ds = data_source(self, **kwargs)
89                         self.data_sources.append(ds)
90
91         def run(self):
92                 # Register signal handlers.
93                 self.register_signal_handler()
94
95                 # Start all data source threads.
96                 for ds in self.data_sources:
97                         ds.start()
98
99                 # Regularly submit all data to disk.
100                 while self.running:
101                         if self.timer.wait():
102                                 self.submit_all()
103
104                 # Wait until all instances are finished.
105                 while self.data_sources:
106                         for ds in self.data_sources[:]:
107                                 if not ds.isAlive():
108                                         log.debug(_("%s is not alive anymore. Removing.") % ds)
109                                         self.data_sources.remove(ds)
110
111                         # Wait a bit.
112                         time.sleep(0.1)
113
114                 log.debug(_("No thread running. Exiting main thread."))
115
116         def submit_all(self):
117                 """
118                         Submit all data right now.
119                 """
120                 log.debug(_("Submitting all data in memory"))
121                 for ds in self.data_sources:
122                         ds._submit()
123
124                 # Schedule the next submit.
125                 self.timer.reset()
126
127         def shutdown(self):
128                 log.debug(_("Received shutdown signal"))
129
130                 self.running = False
131                 if self.timer:
132                         self.timer.cancel()
133
134                 # Propagating shutdown to all threads.
135                 for ds in self.data_sources:
136                         ds.shutdown()
137
138         def register_signal_handler(self):
139                 for s in (signal.SIGTERM, signal.SIGINT, signal.SIGUSR1):
140                         log.debug(_("Registering signal %d") % s)
141
142                 signal.signal(s, self.signal_handler)
143
144         def signal_handler(self, sig, *args, **kwargs):
145                 log.info(_("Caught signal %d") % sig)
146
147                 if sig in (signal.SIGTERM, signal.SIGINT):
148                         # Shutdown this application.
149                         self.shutdown()
150
151                 elif sig == signal.SIGUSR1:
152                         # Submit all data.
153                         self.submit_all()
154
155         @property
156         def graph_default_arguments(self):
157                 return GRAPH_DEFAULT_ARGUMENTS