Completed
Branch master (2f3d56)
by Kenny
01:12
created

initialize()   C

Complexity

Conditions 7

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

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