1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
"""Plumd main entry point.""" |
3
|
|
|
import os |
4
|
|
|
import sys |
5
|
|
|
import time |
6
|
|
|
import signal |
7
|
|
|
import logging |
8
|
|
|
|
9
|
|
|
import click |
|
|
|
|
10
|
|
|
|
11
|
|
|
import plumd |
12
|
|
|
import plumd.util |
13
|
|
|
import plumd.config |
14
|
|
|
from plumd.server.null import NullServer |
15
|
|
|
|
16
|
|
|
PY3 = sys.version_info > (3,) |
17
|
|
|
|
18
|
|
|
__author__ = 'Kenny Freeman' |
19
|
|
|
__email__ = '[email protected]' |
20
|
|
|
__license__ = "ISCL" |
21
|
|
|
__docformat__ = 'reStructuredText' |
22
|
|
|
|
23
|
|
|
|
24
|
|
|
def get_config(cfile): |
25
|
|
|
"""Load configuration from cfile and return a Conf instance from it. |
26
|
|
|
|
27
|
|
|
:param cfile: Full path to a configuration file. |
28
|
|
|
:type cfile: str |
29
|
|
|
:raises: plumd.ConfigError for invalid configuration |
30
|
|
|
:rtype: plumd.config.conf |
31
|
|
|
""" |
32
|
|
|
# get a simple configuration helper object |
33
|
|
|
config = None |
34
|
|
|
try: |
35
|
|
|
config = plumd.config.Conf(cfile).defaults(plumd.DEFAULT_CONFIG) |
36
|
|
|
except plumd.ConfigError as exc: |
37
|
|
|
err = "configuration failed to load: {0} : {1}\n" |
38
|
|
|
sys.stderr.write(err.format(cfile, exc)) |
39
|
|
|
sys.exit(1) |
40
|
|
|
|
41
|
|
|
# ensure log level is known |
42
|
|
|
llevel = config.get('log.level') |
43
|
|
|
if llevel not in plumd.LOG_LEVELS: |
44
|
|
|
llevels = " ".join(list(plumd.LOG_LEVELS.keys())) |
45
|
|
|
sys.stderr.write("unkonwn log level: {0}\n".format(llevel)) |
46
|
|
|
sys.stderr.write("valid log levels are: {0}\n".format(llevels)) |
47
|
|
|
sys.exit(1) |
48
|
|
|
|
49
|
|
|
# all done, return our config |
50
|
|
|
return config |
51
|
|
|
|
52
|
|
|
|
53
|
|
|
def initialize(config): |
54
|
|
|
"""Do various init things with the config instance. |
55
|
|
|
|
56
|
|
|
:param config: a :class:`plumd.config.conf` instance |
57
|
|
|
:type config: plumd.config.conf |
58
|
|
|
""" |
59
|
|
|
# set the configured timezone, from: |
60
|
|
|
# http://stackoverflow.com/questions/6321160/python-logging-how-to-set-time-to-gmt |
61
|
|
|
timezone = config.get('tz') |
62
|
|
|
if timezone: |
63
|
|
|
os.environ['TZ'] = timezone |
64
|
|
|
time.tzset() |
65
|
|
|
|
66
|
|
|
# add hostname metadata |
67
|
|
|
if config.get('meta.hostname'): |
68
|
|
|
meta = config.get('meta') |
69
|
|
|
# meta is a list of single item {'key': 'value'} dicts |
70
|
|
|
for entry in meta: |
71
|
|
|
for key, val in entry.items(): |
72
|
|
|
if key == 'hostname': |
73
|
|
|
err = "meta.hostname: hostname in meta already: {0} : {1}\n" |
74
|
|
|
sys.stderr.write(err.format(val, config.path)) |
75
|
|
|
sys.exit(1) |
76
|
|
|
meta.append({'hostname': plumd.util.get_hostname()}) |
77
|
|
|
config.set_conf('meta', meta) |
78
|
|
|
|
79
|
|
|
return config |
80
|
|
|
|
81
|
|
|
|
82
|
|
|
def get_log(config): |
83
|
|
|
"""Return a configured logger. |
84
|
|
|
|
85
|
|
|
:param config: a :class:`plumd.config.conf` instance |
86
|
|
|
:type config: plumd.config.conf |
87
|
|
|
:param src: all log entries generated are tagged with src=<src> |
88
|
|
|
:type src: str |
89
|
|
|
|
90
|
|
|
:rtype: logging.Logger |
91
|
|
|
""" |
92
|
|
|
# create logger |
93
|
|
|
handler = logging.StreamHandler(sys.stdout) |
94
|
|
|
formatter = logging.Formatter(config.get('log.format')) |
95
|
|
|
handler.setFormatter(formatter) |
96
|
|
|
stdlog = logging.getLogger() |
97
|
|
|
stdlog.addHandler(handler) |
98
|
|
|
stdlog.setLevel(plumd.LOG_LEVELS[config.get('log.level')]) |
99
|
|
|
return stdlog |
100
|
|
|
|
101
|
|
|
|
102
|
|
|
@click.group() |
103
|
|
|
def cli(): |
104
|
|
|
"""Main entry point for plumd.""" |
105
|
|
|
# placeholder function to allow sub-commands |
106
|
|
|
pass |
107
|
|
|
|
108
|
|
|
|
109
|
|
|
@cli.command() |
110
|
|
|
@click.option("--cdir", help="Configuration directory to output to", type=click.Path()) |
|
|
|
|
111
|
|
|
@click.option("--mode", default=plumd.DEFAULT_SERVER, help="What mode to run in", type=click.Choice(['null', 'prometheus'])) |
112
|
|
|
def generate(cdir, mode): |
|
|
|
|
113
|
|
|
"""Create default configurations under the specified path.""" |
114
|
|
|
# todo - create functionality to support this |
115
|
|
|
print("todo..") |
116
|
|
|
# generate configuration file from defaults, save to plumd.yaml |
117
|
|
|
print("gen main config here") |
118
|
|
|
# create Conf object from config |
119
|
|
|
print("load main config here") |
120
|
|
|
# scan for available plugins |
121
|
|
|
print("scan for plugins here") |
122
|
|
|
# create configurations with name only for each reader, all render plugins |
123
|
|
|
print("generate config for each reader plugin") |
124
|
|
|
# create configurations with name only for each render |
125
|
|
|
print("generate config for each render plugin") |
126
|
|
|
# create configurations with name only for each writer, first render plugin found |
127
|
|
|
print("generate config for each writer plugin") |
128
|
|
|
# load all plugins |
129
|
|
|
print("load all plugins here") |
130
|
|
|
# write all configurations back to dir |
131
|
|
|
print("write all configurations here") |
132
|
|
|
|
133
|
|
|
|
134
|
|
|
|
135
|
|
|
|
136
|
|
|
@cli.command() |
137
|
|
|
@click.option("--cfile", default=plumd.DEFAULT_CONFIG_FILE, help="Main configuration filename", type=click.Path()) |
138
|
|
|
@click.option("--mode", default=plumd.DEFAULT_SERVER, help="What mode to run in", type=click.Choice(['null', 'prometheus'])) |
139
|
|
|
def test(cfile, mode): |
140
|
|
|
"""Test the provided configuration and exit.""" |
141
|
|
|
# assumption: if we can load a server without Exceptions, then the config |
142
|
|
|
# is likely OK. |
143
|
|
|
|
144
|
|
|
# get our configuration |
145
|
|
|
config = get_config(cfile) |
146
|
|
|
|
147
|
|
|
# ensure log level is known |
148
|
|
|
llevel = config.get('log.level') |
149
|
|
|
if llevel not in plumd.LOG_LEVELS: |
150
|
|
|
llevels = " ".join(list(plumd.LOG_LEVELS.keys())) |
151
|
|
|
sys.stderr.write("unkonwn log level: {0}\n".format(llevel)) |
152
|
|
|
sys.stderr.write("valid log levels are: {0}\n".format(llevels)) |
153
|
|
|
sys.exit(1) |
154
|
|
|
|
155
|
|
|
# initialize it |
156
|
|
|
config = initialize(config) |
157
|
|
|
|
158
|
|
|
# create a logger |
159
|
|
|
log = get_log(config=config) |
160
|
|
|
vinfo = sys.version_info |
161
|
|
|
ver_str = "{0}.{1}.{2}".format(vinfo[0], vinfo[1], vinfo[2]) |
162
|
|
|
msg = "starting - running on python {0}".format(ver_str) |
163
|
|
|
log.info(msg) |
164
|
|
|
|
165
|
|
|
srv = None |
166
|
|
|
if mode == "null": |
167
|
|
|
srv = NullServer(log, config) |
168
|
|
|
elif mode == "prometheus": |
169
|
|
|
log.error("not implemented: mode=prometheus") |
170
|
|
|
sys.exit(1) |
171
|
|
|
srv.stop() |
172
|
|
|
|
173
|
|
|
log.info("Configuration and plugins loaded OK") |
174
|
|
|
sys.exit(0) |
175
|
|
|
|
176
|
|
|
|
177
|
|
|
@cli.command() |
178
|
|
|
@click.option("--cfile", default=plumd.DEFAULT_CONFIG_FILE, help="Main configuration filename", type=click.Path()) |
179
|
|
|
@click.option("--mode", default=plumd.DEFAULT_SERVER, help="What mode to run in", type=click.Choice(['null', 'prometheus'])) |
180
|
|
|
def run(cfile, mode): |
181
|
|
|
"""Main entry point for plumd. |
182
|
|
|
|
183
|
|
|
:param config: Full path to the main configuration file. |
184
|
|
|
:type config: str |
185
|
|
|
:param mode: The 'server' mode to run in. |
186
|
|
|
:type mode: str |
187
|
|
|
""" |
188
|
|
|
# setup signal handling |
189
|
|
|
sig_wait = [signal.SIGINT, signal.SIGTERM, signal.SIGQUIT] |
190
|
|
|
sig_ignore = [signal.SIGUSR1, signal.SIGUSR2, signal.SIGHUP] |
191
|
|
|
swait = plumd.util.SignalWaiter(sig_wait=sig_wait, sig_ignore=sig_ignore) |
192
|
|
|
|
193
|
|
|
# get our configuration |
194
|
|
|
config = get_config(cfile) |
195
|
|
|
|
196
|
|
|
# ensure log level is known |
197
|
|
|
llevel = config.get('log.level') |
198
|
|
|
if llevel not in plumd.LOG_LEVELS: |
199
|
|
|
llevels = " ".join(list(plumd.LOG_LEVELS.keys())) |
200
|
|
|
sys.stderr.write("unkonwn log level: {0}\n".format(llevel)) |
201
|
|
|
sys.stderr.write("valid log levels are: {0}\n".format(llevels)) |
202
|
|
|
sys.exit(1) |
203
|
|
|
|
204
|
|
|
# initialize it |
205
|
|
|
config = initialize(config) |
206
|
|
|
|
207
|
|
|
# create a logger |
208
|
|
|
log = get_log(config=config) |
209
|
|
|
vinfo = sys.version_info |
210
|
|
|
ver_str = "{0}.{1}.{2}".format(vinfo[0], vinfo[1], vinfo[2]) |
211
|
|
|
msg = "starting - running on python {0}".format(ver_str) |
212
|
|
|
log.info(msg) |
213
|
|
|
|
214
|
|
|
srv = None |
215
|
|
|
if mode == "null": |
216
|
|
|
srv = NullServer(log, config) |
217
|
|
|
elif mode == "prometheus": |
218
|
|
|
log.error("not implemented: mode=prometheus") |
219
|
|
|
sys.exit(1) |
220
|
|
|
|
221
|
|
|
# start server |
222
|
|
|
srv.start() |
223
|
|
|
|
224
|
|
|
# all set, now wait for the exit signal |
225
|
|
|
log.info("running") |
226
|
|
|
swait.wait() |
227
|
|
|
|
228
|
|
|
# stop readers/writers/bridge |
229
|
|
|
log.info("stopping") |
230
|
|
|
srv.stop() |
231
|
|
|
|
232
|
|
|
# all done, exit |
233
|
|
|
log.info("exiting") |
234
|
|
|
sys.exit(0) |
235
|
|
|
|
236
|
|
|
|
237
|
|
|
if __name__ == "__main__": |
238
|
|
|
cli() |
239
|
|
|
|
This can be caused by one of the following:
1. Missing Dependencies
This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.
2. Missing __init__.py files
This error could also result from missing
__init__.py
files in your module folders. Make sure that you place one file in each sub-folder.