Files
anvil/devstack/cfg.py
2012-04-10 11:05:13 -07:00

188 lines
6.8 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (C) 2012 Yahoo! Inc. All Rights Reserved.
#
# 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
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import io
import re
import iniparse
from devstack import cfg_helpers
from devstack import date
from devstack import env
from devstack import exceptions as excp
from devstack import log as logging
from devstack import settings
from devstack import shell as sh
from devstack import utils
LOG = logging.getLogger("devstack.cfg")
ENV_PAT = re.compile(r"^\s*\$\{([\w\d]+):\-(.*)\}\s*$")
SUB_MATCH = re.compile(r"(?:\$\(([\w\d]+):([\w\d]+))\)")
CACHE_MSG = "(value will now be internally cached)"
def get_config(cfg_fn=None):
if not cfg_fn:
cfg_fn = sh.canon_path(settings.STACK_CONFIG_LOCATION)
return StackConfigParser(fns=[cfg_fn])
class IgnoreMissingConfigParser(iniparse.RawConfigParser):
DEF_INT = 0
DEF_FLOAT = 0.0
DEF_BOOLEAN = False
DEF_BASE = None
def __init__(self, cs=True, fns=None):
iniparse.RawConfigParser.__init__(self)
if cs:
# Make option names case sensitive
# See: http://docs.python.org/library/configparser.html#ConfigParser.RawConfigParser.optionxform
self.optionxform = str
if fns:
for f in fns:
self.read(f)
def get(self, section, option):
value = self.DEF_BASE
try:
value = iniparse.RawConfigParser.get(self, section, option)
except iniparse.NoSectionError:
pass
except iniparse.NoOptionError:
pass
return value
def set(self, section, option, value):
if not self.has_section(section) and section.lower() != 'default':
self.add_section(section)
iniparse.RawConfigParser.set(self, section, option, value)
def remove_option(self, section, option):
if self.has_option(section, option):
iniparse.RawConfigParser.remove_option(self, section, option)
def getboolean(self, section, option):
if not self.has_option(section, option):
return self.DEF_BOOLEAN
return iniparse.RawConfigParser.getboolean(self, section, option)
def getfloat(self, section, option):
if not self.has_option(section, option):
return self.DEF_FLOAT
return iniparse.RawConfigParser.getfloat(self, section, option)
def getint(self, section, option):
if not self.has_option(section, option):
return self.DEF_INT
return iniparse.RawConfigParser.getint(self, section, option)
def stringify(self, fn=None):
contents = ''
with io.BytesIO() as outputstream:
self.write(outputstream)
outputstream.flush()
contents = add_header(fn, outputstream.getvalue())
return contents
class StackConfigParser(IgnoreMissingConfigParser):
def __init__(self, cs=True, fns=None):
IgnoreMissingConfigParser.__init__(self, cs, fns)
self.configs_fetched = dict()
def _resolve_value(self, section, option, value_gotten):
if section == 'host' and option == 'ip':
LOG.debug("Host ip from configuration/environment was empty, programatically attempting to determine it.")
value_gotten = utils.get_host_ip()
LOG.debug("Determined your host ip to be: %r" % (value_gotten))
return value_gotten
def getdefaulted(self, section, option, default_val):
val = self.get(section, option)
if not val or not val.strip():
LOG.debug("Value %r found was not good enough, returning provided default '%s'" % (val, default_val))
return default_val
return val
def get(self, section, option):
key = cfg_helpers.make_id(section, option)
if key in self.configs_fetched:
value = self.configs_fetched.get(key)
LOG.debug("Fetched cached value '%s' for param %r" % (value, key))
else:
LOG.debug("Fetching value for param %r" % (key))
gotten_value = self._get_bashed(section, option)
value = self._resolve_value(section, option, gotten_value)
LOG.debug("Fetched %r for %r %s" % (value, key, CACHE_MSG))
self.configs_fetched[key] = value
return value
def set(self, section, option, value):
key = cfg_helpers.make_id(section, option)
LOG.audit("Setting config value '%s' for param %r" % (value, key))
self.configs_fetched[key] = value
IgnoreMissingConfigParser.set(self, section, option, value)
def _resolve_replacements(self, value):
LOG.debug("Performing simple replacement on %r", value)
#allow for our simple replacement to occur
def replacer(match):
section = match.group(1)
option = match.group(2)
return self.getdefaulted(section, option, '')
return SUB_MATCH.sub(replacer, value)
def _get_bashed(self, section, option):
value = IgnoreMissingConfigParser.get(self, section, option)
if value is None:
return value
extracted_val = ''
mtch = ENV_PAT.match(value)
if mtch:
env_key = mtch.group(1).strip()
def_val = mtch.group(2).strip()
if not def_val and not env_key:
msg = "Invalid bash-like value %r" % (value)
raise excp.BadParamException(msg)
env_value = env.get_key(env_key)
if env_value is None:
LOG.debug("Extracting value from config provided default value %r" % (def_val))
extracted_val = self._resolve_replacements(def_val)
LOG.debug("Using config provided default value %r (no environment key)" % (extracted_val))
else:
extracted_val = env_value
LOG.debug("Using enviroment provided value %r" % (extracted_val))
else:
extracted_val = value
LOG.debug("Using raw config provided value %r" % (extracted_val))
return extracted_val
def add_header(fn, contents):
lines = list()
if not fn:
fn = "???"
lines.append('# Adjusted source file %s' % (fn.strip()))
lines.append("# On %s" % (date.rcf8222date()))
lines.append("# By user %s, group %s" % (sh.getuser(), sh.getgroupname()))
lines.append("")
if contents:
lines.append(contents)
return utils.joinlinesep(*lines)