Completed
Push — master ( da3b73...3279ec )
by Kenny
01:17
created

get_plugins()   B

Complexity

Conditions 5

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
c 0
b 0
f 0
dl 0
loc 31
rs 8.0894
1
# -*- coding: utf-8 -*-
2
3
__author__ = 'Kenny Freeman'
4
__email__ = '[email protected]'
5
__license__     = "ISCL"
6
__docformat__   = 'reStructuredText'
7
8
import os
9
import sys
10
import time
11
import signal
12
import random
13
import logging
14
import argparse
15
import threading
16
17
import plumd
18
import plumd.util
19
import plumd.config
20
import plumd.plugins.load
21
22
23
def get_config():
24
    """Returns a conf object - either from the default file or cmd line arg.
25
26
    :rtype: plumd.config.conf
27
    """
28
    descr = """plumd: measures system and service metrics."""
29
    # get cli options - currently just the path to this instances config file
30
    argp = argparse.ArgumentParser(description=descr)
31
    argp.add_argument('--config', metavar='config', type=str,
32
                      nargs='?', default=plumd.DEFAULT_CONFIG_FILE,
33
                      help='main configuration file in yaml format')
34
    args = argp.parse_args()
35
36
    # get a simple configuration helper object
37
    config = None
38
    try:
39
        config = plumd.config.Conf(args.config).defaults(plumd.DEFAULT_CONFIG)
40
    except plumd.ConfigError as e:
41
        sys.stderr.write("configuration failed to load: {0}\n".format(e))
42
        sys.exit(1)
43
44
    # ensure log level is known
45
    llevel = config.get('log.level')
46
    if llevel not in plumd.LOG_LEVELS:
47
        llevels =" ".join(plumd.LOG_LEVELS.keys())
48
        sys.stderr.write("unkonwn log level: {0}\n".format(llevel))
49
        sys.stderr.write("valid log levels are: {0}\n".format(llevels))
50
        sys.exit(1)
51
52
    # all done, return our config
53
    return config
54
55
56
def initialize(config):
57
    """Do various initilization things.
58
59
    :param config: a :class:`plumd.config.conf` instance
60
    :type config: plumd.config.conf
61
    """
62
    # set the configured timezone, from:
63
    # http://stackoverflow.com/questions/6321160/python-logging-how-to-set-time-to-gmt
64
    tz = config.get('tz')
65
    if tz:
66
        try:
67
            os.environ['TZ'] = tz
68
            time.tzset()
69
        except Exception as e:
70
            err = "unable to set timezone: {0} : {1}\n"
71
            sys.stderr.write(err.format(tz, e))
72
            sys.exit(1)
73
74
    # add hostname metadata
75
    if config.get('meta.hostname'):
76
        meta = config.get('meta')
77
        if 'hostname' not in meta:
78
            meta['hostname'] = plumd.util.get_hostname()
79
            config.set_conf('meta', meta)
80
        else:
81
            err = "meta.hostname: hostname already set in config: {0} : {1}\n"
82
            sys.stderr.write(err.format(meta['hostname'], config.path))
83
            sys.exit(1)
84
85
    return config
86
87
88
def get_log(config, src):
89
    """Returns a configured logger.
90
91
    :param config: a :class:`plumd.config.conf` instance
92
    :type config: plumd.config.conf
93
    :param src: all log entries generated are tagged with src=<src>
94
    :type src: str
95
96
    :rtype: structlog.logger
97
    """
98
    # create logger
99
    handler = logging.StreamHandler(sys.stdout)
100
    formatter = logging.Formatter('[%(asctime)s] %(levelname)s %(message)s')
101
    handler.setFormatter(formatter)
102
    stdlog = logging.getLogger()
103
    stdlog.addHandler(handler)
104
    stdlog.setLevel(plumd.LOG_LEVELS[config.get('log.level')])
105
    return stdlog
106
107
108
def get_plugins(config, log):
109
    """Returns a plugin loader object with plugins already started.
110
111
    :param config: a :class:`plumd.config.conf` instance
112
    :type config: plumd.config.conf
113
    :param log: a structlog logging instance
114
    :type log: structlog
115
116
    :rtype: plumd.load.PluginLoader"""
117
    # load reader and writer plugins and start
118
    log.info("loading plugins")
119
    psys = None
120
    # several things can go wrong loading plugins
121
    try:
122
        psys = plumd.plugins.load.PluginLoader(log, config)
123
    except plumd.PluginLoadError as e:
124
        log.critical("plugin load failed: {0}".format(e))
125
        sys.exit(1)
126
    except plumd.ConfigError as e:
127
        log.critical("invalid plugin config: {0}".format(e))
128
        sys.exit(1)
129
    except plumd.DuplicatePlugin as e:
130
        log.critical("duplicate plugin: {0}".format(e))
131
        sys.exit(1)
132
    else:
133
        log.info("plugins loaded")
134
        time.sleep(random.randrange(int(config.get('delay.startup'))))
135
        psys.start()
136
137
    # all done, return the PluginLoader object
138
    return psys
139
140
141
def main():
142
    """Main entry point for plumd."""
143
144
    # setup signal handling
145
    sig_wait = [ signal.SIGINT, signal.SIGTERM, signal.SIGQUIT ]
146
    sig_ignore = [ signal.SIGUSR1, signal.SIGUSR2, signal.SIGHUP ]
147
    swait = plumd.util.SignalWaiter(sig_wait=sig_wait, sig_ignore=sig_ignore)
148
149
    # get a configuration helper object
150
    config = get_config()
151
152
    # initialize it
153
    config = initialize(config)
154
155
    # create a logger
156
    log = get_log(config=config, src="main")
157
    log.info("starting")
158
159
    # get the plugin loader with all plugins started
160
    psys = get_plugins(log=log, config=config)
161
162
    # check to ensure we've loaded a sane configuration
163
    # is at least one reader/writer plugin loaded?
164
    if psys.nplugins < 1:
165
        log.error("exiting: no plugins loaded")
166
        sys.exit(1)
167
    # writer plugin?
168
    elif psys.nwriters < 1:
169
        log.error("exiting: no writers loaded")
170
        sys.exit(1)
171
    # reader plugin?
172
    elif psys.nreaders < 1:
173
        log.error("exiting: no readers loaded")
174
        sys.exit(1)
175
176
    # all set, now wait for the exit signal
177
    log.info("running")
178
    swait.wait()
179
180
    # stop readers/writers/bridge
181
    log.info("stopping")
182
    psys.stop()
183
184
    # all done, exit
185
    log.info("exiting")
186
    sys.exit(0)
187
188
189
if __name__ == "__main__":
190
    main()
191