2
# vim: tabstop=4 shiftwidth=4 softtabstop=4
4
# Copyright 2011 OpenStack LLC.
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
11
# http://www.apache.org/licenses/LICENSE-2.0
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
20
Routines for configuring Glance
26
import logging.handlers
32
from paste import deploy
34
import glance.common.exception as exception
36
DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s"
37
DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
40
def parse_options(parser, cli_args=None):
42
Returns the parsed CLI options, command to run and its arguments, merged
43
with any same-named options found in a configuration file.
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.
49
The reason that the option values are returned as strings only is that
50
ConfigParser and paste.deploy only accept string values...
52
:param parser: The option parser
53
:param cli_args: (Optional) Set of arguments to process. If not present,
55
:retval tuple of (options, args)
58
(options, args) = parser.parse_args(cli_args)
60
return (vars(options), args)
63
def add_common_options(parser):
65
Given a supplied optparse.OptionParser, adds an OptionGroup that
66
represents all common configuration options.
68
:param parser: optparse.OptionParser
70
help_text = "The following configuration options are common to "\
71
"all glance programs."
73
group = optparse.OptionGroup(parser, "Common Options", help_text)
74
group.add_option('-v', '--verbose', default=False, dest="verbose",
76
help="Print more verbose output")
77
group.add_option('-d', '--debug', default=False, dest="debug",
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)
89
def add_log_options(parser):
91
Given a supplied optparse.OptionParser, adds an OptionGroup that
92
represents all the configuration options around logging.
94
:param parser: optparse.OptionParser
96
help_text = "The following configuration options are specific to logging "\
97
"functionality for this program."
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. "
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)
119
def setup_logging(options, conf):
121
Sets up the logging options for a log with supplied name
123
:param options: Mapping of typed option key/values
124
:param conf: Mapping of untyped key/values from config file
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'])
133
raise RuntimeError("Unable to locate specified logging "
134
"config file: %s" % options['log_config'])
136
debug = options.get('debug', False)
137
verbose = options.get('verbose', False)
138
root_logger = logging.root
140
root_logger.setLevel(logging.DEBUG)
142
root_logger.setLevel(logging.INFO)
144
root_logger.setLevel(logging.WARNING)
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)
154
logfile = options.get('log_file')
156
logfile = conf.get('log_file')
159
logdir = options.get('log_dir')
161
logdir = conf.get('log_dir')
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)
169
handler = logging.StreamHandler(sys.stdout)
170
handler.setFormatter(formatter)
171
root_logger.addHandler(handler)
174
def find_config_file(options, args):
176
Return the first config file found.
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:
188
:retval Full path to config file, or None if no config file found
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'])
196
if os.path.exists(args[0]):
197
return fix_path(args[0])
199
# Handle standard directory search for glance.conf
200
config_file_dirs = [fix_path(os.getcwd()),
201
fix_path(os.path.join('~', '.glance')),
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):
212
def load_paste_config(app_name, options, args):
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.
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:
227
:param app_name: Name of the application to load config for, or None.
228
None signifies to only load the [DEFAULT] section of
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)
234
:raises RuntimeError when config file cannot be located or there was a
235
problem loading the configuration file.
237
conf_file = find_config_file(options, args)
239
raise RuntimeError("Unable to locate any configuration file. "
240
"Cannot load application %s" % app_name)
242
conf = deploy.appconfig("config:%s" % conf_file, name=app_name)
243
return conf_file, conf
245
raise RuntimeError("Error trying to load config %s: %s"
249
def load_paste_app(app_name, options, args):
251
Builds and returns a WSGI app from a paste config file.
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:
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:]
267
:raises RuntimeError when config file cannot be located or application
268
cannot be loaded from config file
270
conf_file, conf = load_paste_config(app_name, options, args)
273
# Setup logging early, supplying both the CLI options and the
274
# configuration mapping from the config file
275
setup_logging(options, conf)
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']
282
# Log the options used when starting if we're in debug mode...
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())
302
def get_option(options, option, **kwargs):
303
if option in options:
304
value = options[option]
305
type_ = kwargs.get('type', 'str')
307
if hasattr(value, 'lower'):
308
return value.lower() == 'true'
313
elif type_ == 'float':
317
elif 'default' in kwargs:
318
return kwargs['default']
320
raise KeyError("option '%s' not found" % option)