~eday/burrow/prototype-conversion

« back to all changes in this revision

Viewing changes to burrowd/__init__.py

  • Committer: Eric Day
  • Date: 2011-03-17 23:42:41 UTC
  • Revision ID: eday@oddments.org-20110317234241-ult80xn9d1lon867
First chunk of code from prototype. Beyond the prototype, configuration, module loading, and log handling was added.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2011 OpenStack LLC.
 
2
#
 
3
# Licensed under the Apache License, Version 2.0 (the "License");
 
4
# you may not use this file except in compliance with the License.
 
5
# You may obtain a copy of the License at
 
6
#
 
7
#     http://www.apache.org/licenses/LICENSE-2.0
 
8
#
 
9
# Unless required by applicable law or agreed to in writing, software
 
10
# distributed under the License is distributed on an "AS IS" BASIS,
 
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
12
# See the License for the specific language governing permissions and
 
13
# limitations under the License.
 
14
 
 
15
'''Main server module for burrow.'''
 
16
 
 
17
import ConfigParser
 
18
import gettext
 
19
import logging
 
20
import logging.config
 
21
import sys
 
22
 
 
23
import eventlet
 
24
 
 
25
import burrowd.config
 
26
 
 
27
# This installs the _(...) function as a built-in so all other modules
 
28
# don't need to.
 
29
gettext.install('burrowd')
 
30
 
 
31
# Default configuration values for this module.
 
32
DEFAULT_BACKEND = 'burrowd.backend.sqlite'
 
33
DEFAULT_FRONTENDS = 'burrowd.frontend.wsgi'
 
34
DEFAULT_THREAD_POOL_SIZE = 1000
 
35
 
 
36
 
 
37
class Burrowd(object):
 
38
    '''Server class for burrow.'''
 
39
 
 
40
    def __init__(self, config_files=[], add_default_log_handler=True):
 
41
        '''Initialize a server using the config files from the given
 
42
        list. This is passed directly to ConfigParser.read(), so
 
43
        files should be in ConfigParser format. This will load all
 
44
        frontend and backend classes from the configuration.'''
 
45
        if len(config_files) > 0:
 
46
            logging.config.fileConfig(config_files)
 
47
        self._config = ConfigParser.ConfigParser()
 
48
        self._config.read(config_files)
 
49
        self.config = burrowd.config.Config(self._config, 'burrowd')
 
50
        self.log = get_logger(self.config)
 
51
        if add_default_log_handler:
 
52
            self._add_default_log_handler()
 
53
        self._import_backend()
 
54
        self._import_frontends()
 
55
 
 
56
    def _add_default_log_handler(self):
 
57
        '''Add a default log handler it one has not been set.'''
 
58
        root_log = logging.getLogger()
 
59
        if len(root_log.handlers) > 0 or len(self.log.handlers) > 0:
 
60
            return
 
61
        handler = logging.StreamHandler()
 
62
        handler.setLevel(logging.DEBUG)
 
63
        log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
 
64
        handler.setFormatter(logging.Formatter(log_format))
 
65
        root_log.addHandler(handler)
 
66
 
 
67
    def _import_backend(self):
 
68
        '''Load backend given in the 'backend' option.'''
 
69
        backend = self.config.get('backend', DEFAULT_BACKEND)
 
70
        config = (self._config, backend)
 
71
        self.backend = import_class(backend, 'Backend')(config)
 
72
 
 
73
    def _import_frontends(self):
 
74
        '''Load frontends given in the 'frontends' option.'''
 
75
        self.frontends = []
 
76
        frontends = self.config.get('frontends', DEFAULT_FRONTENDS)
 
77
        for frontend in frontends.split(','):
 
78
            frontend = frontend.split(':')
 
79
            if len(frontend) == 1:
 
80
                frontend.append(None)
 
81
            config = (self._config, frontend[0], frontend[1])
 
82
            frontend = import_class(frontend[0], 'Frontend')
 
83
            frontend = frontend(config, self.backend)
 
84
            self.frontends.append(frontend)
 
85
 
 
86
    def run(self):
 
87
        '''Create the thread pool and start the main server loop. Wait
 
88
        for the pool to complete, but possibly run forever if the
 
89
        frontends and backend never remove threads.'''
 
90
        thread_pool_size = self.config.getint('thread_pool_size',
 
91
            DEFAULT_THREAD_POOL_SIZE)
 
92
        thread_pool = eventlet.GreenPool(size=int(thread_pool_size))
 
93
        self.backend.run(thread_pool)
 
94
        for frontend in self.frontends:
 
95
            frontend.run(thread_pool)
 
96
        self.log.info(_('Waiting for all threads to exit'))
 
97
        try:
 
98
            thread_pool.waitall()
 
99
        except KeyboardInterrupt:
 
100
            pass
 
101
 
 
102
 
 
103
class Module(object):
 
104
    '''Common module class for burrow.'''
 
105
 
 
106
    def __init__(self, config):
 
107
        self.config = burrowd.config.Config(*config)
 
108
        self.log = get_logger(self.config)
 
109
        self.log.debug(_('Module created'))
 
110
 
 
111
 
 
112
def get_logger(config):
 
113
    '''Create a logger from the given config.'''
 
114
    log = logging.getLogger(config.section)
 
115
    log_level = config.get('log_level', 'DEBUG')
 
116
    log_level = logging.getLevelName(log_level)
 
117
    if isinstance(log_level, int):
 
118
        log.setLevel(log_level)
 
119
    return log
 
120
 
 
121
 
 
122
def import_class(module_name, class_name=None):
 
123
    '''Import a class given a full module.class name.'''
 
124
    if class_name is None:
 
125
        module_name, _separator, class_name = module_name.rpartition('.')
 
126
    try:
 
127
        __import__(module_name)
 
128
        return getattr(sys.modules[module_name], class_name)
 
129
    except (ImportError, ValueError, AttributeError), exception:
 
130
        raise ImportError(_('Class %s.%s cannot be found (%s)') %
 
131
            (module_name, class_name, exception))