~yolanda.robla/glance/precise-security

« back to all changes in this revision

Viewing changes to glance/common/config.py

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2011-04-12 09:52:06 UTC
  • mto: (50.1.2 precise-proposed)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20110412095206-8ynvol4gw0phuu30
Tags: upstream-2011.2~bzr108
ImportĀ upstreamĀ versionĀ 2011.2~bzr108

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
3
 
 
4
# Copyright 2011 OpenStack LLC.
 
5
# All Rights Reserved.
 
6
#
 
7
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
8
#    not use this file except in compliance with the License. You may obtain
 
9
#    a copy of the License at
 
10
#
 
11
#         http://www.apache.org/licenses/LICENSE-2.0
 
12
#
 
13
#    Unless required by applicable law or agreed to in writing, software
 
14
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
15
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
16
#    License for the specific language governing permissions and limitations
 
17
#    under the License.
 
18
 
 
19
"""
 
20
Routines for configuring Glance
 
21
"""
 
22
 
 
23
import ConfigParser
 
24
import logging
 
25
import logging.config
 
26
import logging.handlers
 
27
import optparse
 
28
import os
 
29
import re
 
30
import sys
 
31
 
 
32
from paste import deploy
 
33
 
 
34
import glance.common.exception as exception
 
35
 
 
36
DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s"
 
37
DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
 
38
 
 
39
 
 
40
def parse_options(parser, cli_args=None):
 
41
    """
 
42
    Returns the parsed CLI options, command to run and its arguments, merged
 
43
    with any same-named options found in a configuration file.
 
44
 
 
45
    The function returns a tuple of (options, args), where options is a
 
46
    mapping of option key/str(value) pairs, and args is the set of arguments
 
47
    (not options) supplied on the command-line.
 
48
 
 
49
    The reason that the option values are returned as strings only is that
 
50
    ConfigParser and paste.deploy only accept string values...
 
51
 
 
52
    :param parser: The option parser
 
53
    :param cli_args: (Optional) Set of arguments to process. If not present,
 
54
                     sys.argv[1:] is used.
 
55
    :retval tuple of (options, args)
 
56
    """
 
57
 
 
58
    (options, args) = parser.parse_args(cli_args)
 
59
 
 
60
    return (vars(options), args)
 
61
 
 
62
 
 
63
def add_common_options(parser):
 
64
    """
 
65
    Given a supplied optparse.OptionParser, adds an OptionGroup that
 
66
    represents all common configuration options.
 
67
 
 
68
    :param parser: optparse.OptionParser
 
69
    """
 
70
    help_text = "The following configuration options are common to "\
 
71
                "all glance programs."
 
72
 
 
73
    group = optparse.OptionGroup(parser, "Common Options", help_text)
 
74
    group.add_option('-v', '--verbose', default=False, dest="verbose",
 
75
                     action="store_true",
 
76
                     help="Print more verbose output")
 
77
    group.add_option('-d', '--debug', default=False, dest="debug",
 
78
                     action="store_true",
 
79
                     help="Print debugging output")
 
80
    group.add_option('--config-file', default=None, metavar="PATH",
 
81
                     help="Path to the config file to use. When not specified "
 
82
                          "(the default), we generally look at the first "
 
83
                          "argument specified to be a config file, and if "
 
84
                          "that is also missing, we search standard "
 
85
                          "directories for a config file.")
 
86
    parser.add_option_group(group)
 
87
 
 
88
 
 
89
def add_log_options(parser):
 
90
    """
 
91
    Given a supplied optparse.OptionParser, adds an OptionGroup that
 
92
    represents all the configuration options around logging.
 
93
 
 
94
    :param parser: optparse.OptionParser
 
95
    """
 
96
    help_text = "The following configuration options are specific to logging "\
 
97
                "functionality for this program."
 
98
 
 
99
    group = optparse.OptionGroup(parser, "Logging Options", help_text)
 
100
    group.add_option('--log-config', default=None, metavar="PATH",
 
101
                     help="If this option is specified, the logging "
 
102
                          "configuration file specified is used and overrides "
 
103
                          "any other logging options specified. Please see "
 
104
                          "the Python logging module documentation for "
 
105
                          "details on logging configuration files.")
 
106
    group.add_option('--log-date-format', metavar="FORMAT",
 
107
                      default=DEFAULT_LOG_DATE_FORMAT,
 
108
                      help="Format string for %(asctime)s in log records. "
 
109
                           "Default: %default")
 
110
    group.add_option('--log-file', default=None, metavar="PATH",
 
111
                      help="(Optional) Name of log file to output to. "
 
112
                           "If not set, logging will go to stdout.")
 
113
    group.add_option("--log-dir", default=None,
 
114
                      help="(Optional) The directory to keep log files in "
 
115
                           "(will be prepended to --logfile)")
 
116
    parser.add_option_group(group)
 
117
 
 
118
 
 
119
def setup_logging(options, conf):
 
120
    """
 
121
    Sets up the logging options for a log with supplied name
 
122
 
 
123
    :param options: Mapping of typed option key/values
 
124
    :param conf: Mapping of untyped key/values from config file
 
125
    """
 
126
 
 
127
    if options.get('log_config', None):
 
128
        # Use a logging configuration file for all settings...
 
129
        if os.path.exists(options['log_config']):
 
130
            logging.config.fileConfig(options['log_config'])
 
131
            return
 
132
        else:
 
133
            raise RuntimeError("Unable to locate specified logging "
 
134
                               "config file: %s" % options['log_config'])
 
135
 
 
136
    debug = options.get('debug', False)
 
137
    verbose = options.get('verbose', False)
 
138
    root_logger = logging.root
 
139
    if debug:
 
140
        root_logger.setLevel(logging.DEBUG)
 
141
    elif verbose:
 
142
        root_logger.setLevel(logging.INFO)
 
143
    else:
 
144
        root_logger.setLevel(logging.WARNING)
 
145
 
 
146
    # Set log configuration from options...
 
147
    # Note that we use a hard-coded log format in the options
 
148
    # because of Paste.Deploy bug #379
 
149
    # http://trac.pythonpaste.org/pythonpaste/ticket/379
 
150
    log_format = options.get('log_format', DEFAULT_LOG_FORMAT)
 
151
    log_date_format = options.get('log_date_format', DEFAULT_LOG_DATE_FORMAT)
 
152
    formatter = logging.Formatter(log_format, log_date_format)
 
153
 
 
154
    logfile = options.get('log_file')
 
155
    if not logfile:
 
156
        logfile = conf.get('log_file')
 
157
 
 
158
    if logfile:
 
159
        logdir = options.get('log_dir')
 
160
        if not logdir:
 
161
            logdir = conf.get('log_dir')
 
162
        if logdir:
 
163
            logfile = os.path.join(logdir, logfile)
 
164
        logfile = logging.FileHandler(logfile)
 
165
        logfile.setFormatter(formatter)
 
166
        logfile.setFormatter(formatter)
 
167
        root_logger.addHandler(logfile)
 
168
    else:
 
169
        handler = logging.StreamHandler(sys.stdout)
 
170
        handler.setFormatter(formatter)
 
171
        root_logger.addHandler(handler)
 
172
 
 
173
 
 
174
def find_config_file(options, args):
 
175
    """
 
176
    Return the first config file found.
 
177
 
 
178
    We search for the paste config file in the following order:
 
179
    * If --config-file option is used, use that
 
180
    * If args[0] is a file, use that
 
181
    * Search for glance.conf in standard directories:
 
182
        * .
 
183
        * ~.glance/
 
184
        * ~
 
185
        * /etc/glance
 
186
        * /etc
 
187
 
 
188
    :retval Full path to config file, or None if no config file found
 
189
    """
 
190
 
 
191
    fix_path = lambda p: os.path.abspath(os.path.expanduser(p))
 
192
    if options.get('config_file'):
 
193
        if os.path.exists(options['config_file']):
 
194
            return fix_path(options['config_file'])
 
195
    elif args:
 
196
        if os.path.exists(args[0]):
 
197
            return fix_path(args[0])
 
198
 
 
199
    # Handle standard directory search for glance.conf
 
200
    config_file_dirs = [fix_path(os.getcwd()),
 
201
                        fix_path(os.path.join('~', '.glance')),
 
202
                        fix_path('~'),
 
203
                        '/etc/glance/',
 
204
                        '/etc']
 
205
 
 
206
    for cfg_dir in config_file_dirs:
 
207
        cfg_file = os.path.join(cfg_dir, 'glance.conf')
 
208
        if os.path.exists(cfg_file):
 
209
            return cfg_file
 
210
 
 
211
 
 
212
def load_paste_config(app_name, options, args):
 
213
    """
 
214
    Looks for a config file to use for an app and returns the
 
215
    config file path and a configuration mapping from a paste config file.
 
216
 
 
217
    We search for the paste config file in the following order:
 
218
    * If --config-file option is used, use that
 
219
    * If args[0] is a file, use that
 
220
    * Search for glance.conf in standard directories:
 
221
        * .
 
222
        * ~.glance/
 
223
        * ~
 
224
        * /etc/glance
 
225
        * /etc
 
226
 
 
227
    :param app_name: Name of the application to load config for, or None.
 
228
                     None signifies to only load the [DEFAULT] section of
 
229
                     the config file.
 
230
    :param options: Set of typed options returned from parse_options()
 
231
    :param args: Command line arguments from argv[1:]
 
232
    :retval Tuple of (conf_file, conf)
 
233
 
 
234
    :raises RuntimeError when config file cannot be located or there was a
 
235
            problem loading the configuration file.
 
236
    """
 
237
    conf_file = find_config_file(options, args)
 
238
    if not conf_file:
 
239
        raise RuntimeError("Unable to locate any configuration file. "
 
240
                            "Cannot load application %s" % app_name)
 
241
    try:
 
242
        conf = deploy.appconfig("config:%s" % conf_file, name=app_name)
 
243
        return conf_file, conf
 
244
    except Exception, e:
 
245
        raise RuntimeError("Error trying to load config %s: %s"
 
246
                           % (conf_file, e))
 
247
 
 
248
 
 
249
def load_paste_app(app_name, options, args):
 
250
    """
 
251
    Builds and returns a WSGI app from a paste config file.
 
252
 
 
253
    We search for the paste config file in the following order:
 
254
    * If --config-file option is used, use that
 
255
    * If args[0] is a file, use that
 
256
    * Search for glance.conf in standard directories:
 
257
        * .
 
258
        * ~.glance/
 
259
        * ~
 
260
        * /etc/glance
 
261
        * /etc
 
262
 
 
263
    :param app_name: Name of the application to load
 
264
    :param options: Set of typed options returned from parse_options()
 
265
    :param args: Command line arguments from argv[1:]
 
266
 
 
267
    :raises RuntimeError when config file cannot be located or application
 
268
            cannot be loaded from config file
 
269
    """
 
270
    conf_file, conf = load_paste_config(app_name, options, args)
 
271
 
 
272
    try:
 
273
        # Setup logging early, supplying both the CLI options and the
 
274
        # configuration mapping from the config file
 
275
        setup_logging(options, conf)
 
276
 
 
277
        # We only update the conf dict for the verbose and debug
 
278
        # flags. Everything else must be set up in the conf file...
 
279
        conf['verbose'] = options['verbose']
 
280
        conf['debug'] = options['debug']
 
281
 
 
282
        # Log the options used when starting if we're in debug mode...
 
283
        if conf['debug']:
 
284
            logger = logging.getLogger(app_name)
 
285
            logger.debug("*" * 80)
 
286
            logger.debug("Configuration options gathered from config file:")
 
287
            logger.debug(conf_file)
 
288
            logger.debug("================================================")
 
289
            items = dict([(k, v) for k, v in conf.items()
 
290
                          if k not in ('__file__', 'here')])
 
291
            for key, value in sorted(items.items()):
 
292
                logger.debug("%(key)-30s %(value)s" % locals())
 
293
            logger.debug("*" * 80)
 
294
        app = deploy.loadapp("config:%s" % conf_file, name=app_name)
 
295
    except (LookupError, ImportError), e:
 
296
        raise RuntimeError("Unable to load %(app_name)s from "
 
297
                           "configuration file %(conf_file)s."
 
298
                           "\nGot: %(e)r" % locals())
 
299
    return conf, app
 
300
 
 
301
 
 
302
def get_option(options, option, **kwargs):
 
303
    if option in options:
 
304
        value = options[option]
 
305
        type_ = kwargs.get('type', 'str')
 
306
        if type_ == 'bool':
 
307
            if hasattr(value, 'lower'):
 
308
                return value.lower() == 'true'
 
309
            else:
 
310
                return value
 
311
        elif type_ == 'int':
 
312
            return int(value)
 
313
        elif type_ == 'float':
 
314
            return float(value)
 
315
        else:
 
316
            return value
 
317
    elif 'default' in kwargs:
 
318
        return kwargs['default']
 
319
    else:
 
320
        raise KeyError("option '%s' not found" % option)