Completed
Push — master ( 08acac...d85769 )
by Kenny
01:15
created

initialize()   B

Complexity

Conditions 6

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 27
rs 7.5384
cc 6
1
# -*- coding: utf-8 -*-
2
"""Plumd main entry point."""
3
import os
4
import sys
5
import time
6
import signal
7
import random
8
import logging
9
import argparse
10
11
import plumd
12
import plumd.util
13
import plumd.config
14
15
PY3 = sys.version_info > (3,)
16
17
__author__ = 'Kenny Freeman'
18
__email__ = '[email protected]'
19
__license__ = "ISCL"
20
__docformat__ = 'reStructuredText'
21
22
23
def get_config():
24
    """Return 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 exc:
41
        sys.stderr.write("configuration failed to load: {0}\n".format(exc))
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(list(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
    timezzone = config.get('tz')
65
    if timezzone:
66
        os.environ['TZ'] = timezzone
67
        time.tzset()
68
69
    # add hostname metadata
70
    if config.get('meta.hostname'):
71
        meta = config.get('meta')
72
        # meta is a list of single item {'key': 'value'} dicts
73
        for entry in meta:
74
            for key, val in entry.items():
75
                if key == 'hostname':
76
                    err = "meta.hostname: hostname in meta already: {0} : {1}\n"
77
                    sys.stderr.write(err.format(val, config.path))
78
                    sys.exit(1)
79
        meta.append({'hostname': plumd.util.get_hostname()})
80
        config.set_conf('meta', meta)
81
82
    return config
83
84
85
def get_log(config):
86
    """Return a configured logger.
87
88
    :param config: a :class:`plumd.config.conf` instance
89
    :type config: plumd.config.conf
90
    :param src: all log entries generated are tagged with src=<src>
91
    :type src: str
92
93
    :rtype: structlog.logger
94
    """
95
    # create logger
96
    handler = logging.StreamHandler(sys.stdout)
97
    formatter = logging.Formatter(config.get('log.format'))
98
    handler.setFormatter(formatter)
99
    stdlog = logging.getLogger()
100
    stdlog.addHandler(handler)
101
    stdlog.setLevel(plumd.LOG_LEVELS[config.get('log.level')])
102
    return stdlog
103
104
105
def get_plugins(config, log):
106
    """Return a plugin loader object with plugins already started.
107
108
    :param config: a :class:`plumd.config.conf` instance
109
    :type config: plumd.config.conf
110
    :param log: a structlog logging instance
111
    :type log: structlog
112
113
    :rtype: plumd.load.PluginLoader
114
    """
115
    # load reader and writer plugins and start
116
    log.info("loading plugins")
117
    psys = None
118
    # several things can go wrong loading plugins
119
    try:
120
        psys = plumd.PluginLoader(log, config)
121
    except plumd.PluginLoadError as exc:
122
        log.critical("plugin load failed: {0}".format(exc))
123
        sys.exit(1)
124
    except plumd.ConfigError as exc:
125
        log.critical("invalid plugin config: {0}".format(exc))
126
        sys.exit(1)
127
    except plumd.DuplicatePlugin as exc:
128
        log.critical("duplicate plugin: {0}".format(exc))
129
        sys.exit(1)
130
    else:
131
        log.info("plugins loaded")
132
        time.sleep(random.randrange(int(config.get('delay.startup'))))
133
        psys.start()
134
    log.debug("main: plugins: {0}".format(psys))
135
136
    # all done, return the PluginLoader object
137
    return psys
138
139
140
def main():
141
    """Main entry point for plumd."""
142
    # setup signal handling
143
    sig_wait = [signal.SIGINT, signal.SIGTERM, signal.SIGQUIT]
144
    sig_ignore = [signal.SIGUSR1, signal.SIGUSR2, signal.SIGHUP]
145
    swait = plumd.util.SignalWaiter(sig_wait=sig_wait, sig_ignore=sig_ignore)
146
147
    # get a configuration helper object
148
    config = get_config()
149
150
    # initialize it
151
    config = initialize(config)
152
153
    # create a logger
154
    log = get_log(config=config)
155
    vinfo = sys.version_info
156
    ver_str = "{0}.{1}.{2}".format(vinfo[0], vinfo[1], vinfo[2])
157
    msg = "starting - running on python {0}".format(ver_str)
158
    log.info(msg)
159
160
    # get the plugin loader with all plugins started
161
    psys = get_plugins(log=log, config=config)
162
163
    # check to ensure we've loaded a sane configuration
164
    # is at least one reader/writer plugin loaded?
165
    if psys.nplugins < 1:
166
        log.error("exiting: no plugins loaded")
167
        sys.exit(1)
168
    # writer plugin?
169
    elif psys.nwriters < 1:
170
        log.error("exiting: no writers loaded")
171
        sys.exit(1)
172
    # reader plugin?
173
    elif psys.nreaders < 1:
174
        log.error("exiting: no readers loaded")
175
        sys.exit(1)
176
177
    # all set, now wait for the exit signal
178
    log.info("running")
179
    swait.wait()
180
181
    # stop readers/writers/bridge
182
    log.info("stopping")
183
    psys.stop()
184
185
    # all done, exit
186
    log.info("exiting")
187
    sys.exit(0)
188
189
190
if __name__ == "__main__":
191
    main()
192