]>
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() | |
b1ea4956 | 40 | self.data_sources = [] |
73db5226 MT |
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 | ||
b1ea4956 MT |
46 | # Add all automatic data sources. |
47 | self.add_autocreate_data_sources() | |
73db5226 MT |
48 | |
49 | log.info(_("Collecty successfully initialized.")) | |
50 | ||
b1ea4956 MT |
51 | def add_autocreate_data_sources(self): |
52 | for data_source in plugins.data_sources: | |
53 | if not hasattr(data_source, "autocreate"): | |
73db5226 MT |
54 | continue |
55 | ||
b1ea4956 | 56 | ret = data_source.autocreate(self) |
73db5226 MT |
57 | if not ret: |
58 | continue | |
59 | ||
60 | if not type(ret) == type([]): | |
61 | ret = [ret,] | |
62 | ||
b1ea4956 MT |
63 | log.debug(_("Data source '%(name)s' registered %(number)s instance(s).") % \ |
64 | { "name" : data_source.name, "number" : len(ret) }) | |
73db5226 | 65 | |
b1ea4956 | 66 | self.data_sources += ret |
73db5226 MT |
67 | |
68 | def read_config(self, config): | |
69 | self.config.read(config) | |
70 | ||
71 | for section in self.config.sections(): | |
72 | try: | |
b1ea4956 MT |
73 | data_source = self.config.get(section, "data_source") |
74 | data_source = plugins.find(data_source) | |
73db5226 MT |
75 | except configparser.NoOptionError: |
76 | raise ConfigError, "Syntax error in configuration: plugin option is missing." | |
77 | except: | |
b1ea4956 | 78 | raise Exception, "Plugin configuration error: Maybe plugin wasn't found? %s" % data_source |
73db5226 MT |
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 | ||
b1ea4956 MT |
88 | ds = data_source(self, **kwargs) |
89 | self.data_sources.append(ds) | |
73db5226 MT |
90 | |
91 | def run(self): | |
92 | # Register signal handlers. | |
93 | self.register_signal_handler() | |
94 | ||
b1ea4956 MT |
95 | # Start all data source threads. |
96 | for ds in self.data_sources: | |
97 | ds.start() | |
73db5226 MT |
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. | |
b1ea4956 MT |
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) | |
73db5226 MT |
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")) | |
b1ea4956 MT |
121 | for ds in self.data_sources: |
122 | ds._submit() | |
73db5226 MT |
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. | |
b1ea4956 MT |
135 | for ds in self.data_sources: |
136 | ds.shutdown() | |
73db5226 MT |
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 |