Allow cfg values to be stored in a templated format

This commit is contained in:
Joshua Harlow 2012-09-25 11:12:10 -07:00
parent f0de6f3dbd
commit 249d4f1355
6 changed files with 63 additions and 37 deletions

View File

@ -34,6 +34,9 @@ LOG = logging.getLogger(__name__)
class StringiferMixin(object):
def __init__(self):
pass
def stringify(self, fn=None):
contents = ''
with io.BytesIO() as outputstream:
@ -43,16 +46,19 @@ class StringiferMixin(object):
return contents
class IgnoreMissingMixin(object):
class ConfigHelperMixin(object):
DEF_INT = 0
DEF_FLOAT = 0.0
DEF_BOOLEAN = False
DEF_BASE = None
def __init__(self, templatize_values=False):
self.templatize_values = templatize_values
def get(self, section, option):
value = self.DEF_BASE
try:
value = super(IgnoreMissingMixin, self).get(section, option)
value = super(ConfigHelperMixin, self).get(section, option)
except NoSectionError:
pass
except NoOptionError:
@ -62,32 +68,41 @@ class IgnoreMissingMixin(object):
def set(self, section, option, value):
if not self.has_section(section) and section.lower() != 'default':
self.add_section(section)
super(IgnoreMissingMixin, self).set(section, option, value)
if self.templatize_values:
real_value = "%" + str(option) + "%"
for c in ['-', ' ', '\t', ':']:
real_value = real_value.replace(c, '_')
value = real_value
super(ConfigHelperMixin, self).set(section, option, value)
def remove_option(self, section, option):
if self.has_option(section, option):
super(IgnoreMissingMixin, self).remove_option(section, option)
super(ConfigHelperMixin, self).remove_option(section, option)
def getboolean(self, section, option):
if not self.has_option(section, option):
return self.DEF_BOOLEAN
return super(IgnoreMissingMixin, self).getboolean(section, option)
return super(ConfigHelperMixin, self).getboolean(section, option)
def getfloat(self, section, option):
if not self.has_option(section, option):
return self.DEF_FLOAT
return super(IgnoreMissingMixin, self).getfloat(section, option)
return super(ConfigHelperMixin, self).getfloat(section, option)
def getint(self, section, option):
if not self.has_option(section, option):
return self.DEF_INT
return super(IgnoreMissingMixin, self).getint(section, option)
return super(ConfigHelperMixin, self).getint(section, option)
def getlist(self, section, option):
return self.get(section, option).split(",")
class BuiltinConfigParser(IgnoreMissingMixin, ConfigParser.RawConfigParser, StringiferMixin):
def __init__(self, cs=True, fns=None, defaults=None):
ConfigParser.RawConfigParser.__init__(self, defaults=defaults)
if cs:
class BuiltinConfigParser(ConfigHelperMixin, ConfigParser.RawConfigParser, StringiferMixin):
def __init__(self, fns=None, templatize_values=False):
ConfigHelperMixin.__init__(self, templatize_values)
ConfigParser.RawConfigParser.__init__(self)
StringiferMixin.__init__(self)
# Make option names case sensitive
# See: http://docs.python.org/library/configparser.html#ConfigParser.RawConfigParser.optionxform
self.optionxform = str
@ -96,10 +111,11 @@ class BuiltinConfigParser(IgnoreMissingMixin, ConfigParser.RawConfigParser, Stri
self.read(f)
class RewritableConfigParser(IgnoreMissingMixin, iniparse.RawConfigParser, StringiferMixin):
def __init__(self, cs=True, fns=None, defaults=None):
iniparse.RawConfigParser.__init__(self, defaults=defaults)
if cs:
class RewritableConfigParser(ConfigHelperMixin, iniparse.RawConfigParser, StringiferMixin):
def __init__(self, fns=None, templatize_values=False):
ConfigHelperMixin.__init__(self, templatize_values)
iniparse.RawConfigParser.__init__(self)
StringiferMixin.__init__(self)
# Make option names case sensitive
# See: http://docs.python.org/library/configparser.html#ConfigParser.RawConfigParser.optionxform
self.optionxform = str
@ -226,3 +242,12 @@ class YamlInterpolator(object):
self.interpolated[root] = self.included[root]
self.interpolated[root] = self._interpolate(self.interpolated[root])
return self.interpolated[root]
def create_parser(cfg_cls, component, fns=None):
templatize_values = component.get_bool_option('template_config')
cfg_opts = {
'fns': fns,
'templatize_values': templatize_values,
}
return cfg_cls(**cfg_opts)

View File

@ -111,7 +111,7 @@ class GlanceInstaller(comp.PythonInstallComponent):
def _config_adjust_registry(self, contents, fn):
params = ghelper.get_shared_params(**self.options)
with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser()
config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream)
config.set('DEFAULT', 'debug', self.get_bool_option('verbose'))
config.set('DEFAULT', 'verbose', self.get_bool_option('verbose'))
@ -132,7 +132,7 @@ class GlanceInstaller(comp.PythonInstallComponent):
**utils.merge_dicts(self.get_option('keystone'),
khelper.get_shared_passwords(self)))
with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser()
config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream)
config.set('filter:authtoken', 'auth_host', params['endpoints']['admin']['host'])
config.set('filter:authtoken', 'auth_port', params['endpoints']['admin']['port'])
@ -150,7 +150,7 @@ class GlanceInstaller(comp.PythonInstallComponent):
def _config_adjust_api(self, contents, fn):
params = ghelper.get_shared_params(**self.options)
with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser()
config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream)
img_store_dir = sh.joinpths(self.get_option('component_dir'), 'images')
config.set('DEFAULT', 'debug', self.get_bool_option('verbose',))
@ -172,7 +172,7 @@ class GlanceInstaller(comp.PythonInstallComponent):
def _config_adjust_logging(self, contents, fn):
with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser()
config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream)
config.set('logger_root', 'level', 'DEBUG')
config.set('logger_root', 'handlers', "devel,production")

View File

@ -144,7 +144,7 @@ class ConfConfigurator(object):
def generate(self, fn):
# Everything built goes in here
nova_conf = Conf(fn)
nova_conf = Conf(fn, self.installer)
# Used more than once so we calculate it ahead of time
hostip = self.installer.get_option('ip')
@ -387,8 +387,9 @@ class ConfConfigurator(object):
# This class represents the data/format of the nova config file
class Conf(object):
def __init__(self, name):
self.backing = cfg.BuiltinConfigParser()
def __init__(self, name, installer):
self.installer = installer
self.backing = cfg.create_parser(cfg.BuiltinConfigParser, self.installer)
self.name = name
def add(self, key, value, *values):

View File

@ -132,7 +132,7 @@ class KeystoneInstaller(comp.PythonInstallComponent):
def _config_adjust_logging(self, contents, fn):
with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser()
config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream)
config.set('logger_root', 'level', 'DEBUG')
config.set('logger_root', 'handlers', "devel,production")
@ -150,7 +150,7 @@ class KeystoneInstaller(comp.PythonInstallComponent):
params = khelper.get_shared_params(**utils.merge_dicts(self.options,
khelper.get_shared_passwords(self)))
with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser()
config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream)
config.set('DEFAULT', 'admin_token', params['service_token'])
config.set('DEFAULT', 'admin_port', params['endpoints']['admin']['port'])

View File

@ -204,7 +204,7 @@ class NovaInstaller(comp.PythonInstallComponent):
khelper.get_shared_passwords(self)))
with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser()
config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream)
config.set('filter:authtoken', 'auth_host', params['endpoints']['admin']['host'])
@ -224,7 +224,7 @@ class NovaInstaller(comp.PythonInstallComponent):
def _config_adjust_logging(self, contents, fn):
with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser()
config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream)
config.set('logger_root', 'level', 'DEBUG')
config.set('logger_root', 'handlers', "stdout")

View File

@ -25,7 +25,7 @@ LOG = logging.getLogger(__name__)
class PhaseRecorder(object):
def __init__(self, fn):
self.fn = fn
self.filename = fn
self.state = None
def _format_contents(self, contents):
@ -36,12 +36,12 @@ class PhaseRecorder(object):
contents = self.list_phases()
contents[what] = utils.iso8601()
yield what
sh.write_file(self.fn, self._format_contents(contents))
sh.write_file(self.filename, self._format_contents(contents))
def unmark(self, what):
contents = self.list_phases()
contents.pop(what, None)
sh.write_file(self.fn, self._format_contents(contents))
sh.write_file(self.filename, self._format_contents(contents))
def __contains__(self, what):
phases = self.list_phases()
@ -55,10 +55,10 @@ class PhaseRecorder(object):
state = {}
# Shell not used to avoid dry-run capturing
try:
with open(self.fn, 'r') as fh:
with open(self.filename, 'r') as fh:
state = utils.load_yaml_text(fh.read())
if not isinstance(state, (dict)):
raise TypeError("Phase file %s expected dictionary root type" % (self.fn))
raise TypeError("Phase file %s expected dictionary root type" % (self.filename))
except IOError:
pass
self.state = state