From db86220abb0861ccb58fb6af57a262c734a18d6e Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Tue, 1 May 2012 10:30:03 +0100 Subject: [PATCH] Use ConfigOpts.find_file() for policy and paste Use cfg's new helper method to find the PasteDeploy and policy config files. The basic behavior is "look alongside the config file" with a fall back to the standard default config paths. In the fairly obscure case where no --config-file is found we use an explicit default paste file taking to account the fact that all the glance apps have their paste config in a single file. Add some new debug logging to make it easier to see which config files are being loaded. Fixes bug 992319 Change-Id: I978c4a8cb6ec6d12aaf746af511ccb572c34b7ee --- glance/api/policy.py | 13 +++-- glance/common/config.py | 17 ++++-- glance/openstack/common/cfg.py | 97 +++++++++++++++++++++++++++------- 3 files changed, 98 insertions(+), 29 deletions(-) diff --git a/glance/api/policy.py b/glance/api/policy.py index 5fcdacd780..a280324724 100644 --- a/glance/api/policy.py +++ b/glance/api/policy.py @@ -18,12 +18,15 @@ """Policy Engine For Glance""" import json +import logging import os.path from glance.common import exception from glance.common import policy from glance.openstack.common import cfg +logger = logging.getLogger(__name__) + class Enforcer(object): """Responsible for loading and enforcing rules""" @@ -58,13 +61,12 @@ def _find_policy_file(conf): if conf.policy_file: return conf.policy_file - matches = cfg.find_config_files('glance', 'policy', '.json') - - try: - return matches[0] - except IndexError: + policy_file = conf.find_file('policy.json') + if not policy_file: raise cfg.ConfigFilesNotFoundError(('policy.json',)) + return policy_file + def _read_policy_file(self): """Read contents of the policy file @@ -72,6 +74,7 @@ def _read_policy_file(self): """ mtime = os.path.getmtime(self.policy_path) if not self.policy_file_contents or mtime != self.policy_file_mtime: + logger.debug(_("Loading policy from %s") % self.policy_path) with open(self.policy_path) as fap: raw_contents = fap.read() self.policy_file_contents = json.loads(raw_contents) diff --git a/glance/common/config.py b/glance/common/config.py index d740cdc732..9e7355edb7 100644 --- a/glance/common/config.py +++ b/glance/common/config.py @@ -46,6 +46,7 @@ def __init__(self, default_config_files=None, **kwargs): version='%%prog %s' % version.version_string(), default_config_files=default_config_files, **kwargs) + self.default_paste_file = self.prog + '-paste.ini' class GlanceCacheConfigOpts(GlanceConfigOpts): @@ -54,6 +55,7 @@ def __init__(self, **kwargs): config_files = cfg.find_config_files(project='glance', prog='glance-cache') super(GlanceCacheConfigOpts, self).__init__(config_files, **kwargs) + self.default_paste_file = 'glance-cache-paste.ini' def setup_logging(conf): @@ -128,12 +130,13 @@ def _get_deployment_flavor(conf): def _get_paste_config_path(conf): paste_suffix = '-paste.ini' conf_suffix = '.conf' - if conf.config_dir: - return os.path.join(conf.config_dir, conf.prog + paste_suffix) - else: + if conf.config_file: # Assume paste config is in a paste.ini file corresponding # to the last config file - return conf.config_file[-1].replace(conf_suffix, paste_suffix) + path = conf.config_file[-1].replace(conf_suffix, paste_suffix) + else: + path = conf.default_paste_file + return conf.find_file(os.path.basename(path)) def _get_deployment_config_file(conf): @@ -174,12 +177,16 @@ def load_paste_app(conf, app_name=None): try: # Setup logging early setup_logging(conf) + logger = logging.getLogger(app_name) + + logger.debug(_("Loading %(app_name)s from %(conf_file)s"), + {'conf_file': conf_file, 'app_name': app_name}) app = wsgi.paste_deploy_app(conf_file, app_name, conf) # Log the options used when starting if we're in debug mode... if conf.debug: - conf.log_opt_values(logging.getLogger(app_name), logging.DEBUG) + conf.log_opt_values(logger, logging.DEBUG) return app except (LookupError, ImportError), e: diff --git a/glance/openstack/common/cfg.py b/glance/openstack/common/cfg.py index 9a0e0313e6..0f25668929 100644 --- a/glance/openstack/common/cfg.py +++ b/glance/openstack/common/cfg.py @@ -1,6 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2011 Red Hat, Inc. +# Copyright 2012 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -325,6 +325,52 @@ class ConfigFileValueError(Error): pass +def _get_config_dirs(project=None): + """Return a list of directors where config files may be located. + + :param project: an optional project name + + If a project is specified, following directories are returned:: + + ~/.${project}/ + ~/ + /etc/${project}/ + /etc/ + + Otherwise, these directories:: + + ~/ + /etc/ + """ + fix_path = lambda p: os.path.abspath(os.path.expanduser(p)) + + cfg_dirs = [ + fix_path(os.path.join('~', '.' + project)) if project else None, + fix_path('~'), + os.path.join('/etc', project) if project else None, + '/etc' + ] + + return filter(bool, cfg_dirs) + + +def _search_dirs(dirs, basename, extension=""): + """Search a list of directories for a given filename. + + Iterator over the supplied directories, returning the first file + found with the supplied name and extension. + + :param dirs: a list of directories + :param basename: the filename, e.g. 'glance-api' + :param extension: the file extension, e.g. '.conf' + :returns: the path to a matching file, or None + """ + for d in dirs: + path = os.path.join(d, '%s%s' % (basename, extension)) + if os.path.exists(path): + return path + + def find_config_files(project=None, prog=None, extension='.conf'): """Return a list of default configuration files. @@ -353,27 +399,12 @@ def find_config_files(project=None, prog=None, extension='.conf'): if prog is None: prog = os.path.basename(sys.argv[0]) - fix_path = lambda p: os.path.abspath(os.path.expanduser(p)) - - cfg_dirs = [ - fix_path(os.path.join('~', '.' + project)) if project else None, - fix_path('~'), - os.path.join('/etc', project) if project else None, - '/etc', - 'etc' - ] - cfg_dirs = filter(bool, cfg_dirs) - - def search_dirs(dirs, basename, extension): - for d in dirs: - path = os.path.join(d, '%s%s' % (basename, extension)) - if os.path.exists(path): - return path + cfg_dirs = _get_config_dirs(project) config_files = [] if project: - config_files.append(search_dirs(cfg_dirs, project, extension)) - config_files.append(search_dirs(cfg_dirs, prog, extension)) + config_files.append(_search_dirs(cfg_dirs, project, extension)) + config_files.append(_search_dirs(cfg_dirs, prog, extension)) return filter(bool, config_files) @@ -1061,6 +1092,34 @@ def enable_interspersed_args(self): This it the default behaviour.""" self._oparser.enable_interspersed_args() + def find_file(self, name): + """Locate a file located alongside the config files. + + Search for a file with the supplied basename in the directories + which we have already loaded config files from and other known + configuration directories. + + The directory, if any, supplied by the config_dir option is + searched first. Then the config_file option is iterated over + and each of the base directories of the config_files values + are searched. Failing both of these, the standard directories + searched by the module level find_config_files() function is + used. The first matching file is returned. + + :param basename: the filename, e.g. 'policy.json' + :returns: the path to a matching file, or None + """ + dirs = [] + if self.config_dir: + dirs.append(self.config_dir) + + for cf in reversed(self.config_file): + dirs.append(os.path.dirname(cf)) + + dirs.extend(_get_config_dirs(self.project)) + + return _search_dirs(dirs, name) + def log_opt_values(self, logger, lvl): """Log the value of all registered opts.