]>
Commit | Line | Data |
---|---|---|
73db5226 MT |
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.instances = [] | |
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 plugins. | |
47 | self.add_autocreate_plugins() | |
48 | ||
49 | log.info(_("Collecty successfully initialized.")) | |
50 | ||
51 | def add_autocreate_plugins(self): | |
52 | for plugin in plugins.registered_plugins: | |
53 | if not hasattr(plugin, "autocreate"): | |
54 | continue | |
55 | ||
56 | ret = plugin.autocreate(self) | |
57 | if not ret: | |
58 | continue | |
59 | ||
60 | if not type(ret) == type([]): | |
61 | ret = [ret,] | |
62 | ||
63 | log.debug(_("Plugin '%(name)s' registered %(number)s instance(s).") % \ | |
64 | { "name" : plugin.name, "number" : len(ret) }) | |
65 | ||
66 | self.instances += ret | |
67 | ||
68 | def read_config(self, config): | |
69 | self.config.read(config) | |
70 | ||
71 | for section in self.config.sections(): | |
72 | try: | |
73 | plugin = self.config.get(section, "plugin") | |
74 | plugin = plugins.find(plugin) | |
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" % plugin | |
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 | i = plugin(self, **kwargs) | |
89 | self.instances.append(i) | |
90 | ||
91 | def run(self): | |
92 | # Register signal handlers. | |
93 | self.register_signal_handler() | |
94 | ||
95 | # Start all plugin instances. | |
96 | for i in self.instances: | |
97 | i.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.instances: | |
106 | for instance in self.instances[:]: | |
107 | if not instance.isAlive(): | |
108 | log.debug(_("%s is not alive anymore. Removing.") % instance) | |
109 | self.instances.remove(instance) | |
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 i in self.instances: | |
122 | i._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 i in self.instances: | |
136 | i.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 |