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): class StringiferMixin(object):
def __init__(self):
pass
def stringify(self, fn=None): def stringify(self, fn=None):
contents = '' contents = ''
with io.BytesIO() as outputstream: with io.BytesIO() as outputstream:
@ -43,16 +46,19 @@ class StringiferMixin(object):
return contents return contents
class IgnoreMissingMixin(object): class ConfigHelperMixin(object):
DEF_INT = 0 DEF_INT = 0
DEF_FLOAT = 0.0 DEF_FLOAT = 0.0
DEF_BOOLEAN = False DEF_BOOLEAN = False
DEF_BASE = None DEF_BASE = None
def __init__(self, templatize_values=False):
self.templatize_values = templatize_values
def get(self, section, option): def get(self, section, option):
value = self.DEF_BASE value = self.DEF_BASE
try: try:
value = super(IgnoreMissingMixin, self).get(section, option) value = super(ConfigHelperMixin, self).get(section, option)
except NoSectionError: except NoSectionError:
pass pass
except NoOptionError: except NoOptionError:
@ -62,47 +68,57 @@ class IgnoreMissingMixin(object):
def set(self, section, option, value): def set(self, section, option, value):
if not self.has_section(section) and section.lower() != 'default': if not self.has_section(section) and section.lower() != 'default':
self.add_section(section) 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): def remove_option(self, section, option):
if self.has_option(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): def getboolean(self, section, option):
if not self.has_option(section, option): if not self.has_option(section, option):
return self.DEF_BOOLEAN return self.DEF_BOOLEAN
return super(IgnoreMissingMixin, self).getboolean(section, option) return super(ConfigHelperMixin, self).getboolean(section, option)
def getfloat(self, section, option): def getfloat(self, section, option):
if not self.has_option(section, option): if not self.has_option(section, option):
return self.DEF_FLOAT return self.DEF_FLOAT
return super(IgnoreMissingMixin, self).getfloat(section, option) return super(ConfigHelperMixin, self).getfloat(section, option)
def getint(self, section, option): def getint(self, section, option):
if not self.has_option(section, option): if not self.has_option(section, option):
return self.DEF_INT 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): class BuiltinConfigParser(ConfigHelperMixin, ConfigParser.RawConfigParser, StringiferMixin):
def __init__(self, cs=True, fns=None, defaults=None): def __init__(self, fns=None, templatize_values=False):
ConfigParser.RawConfigParser.__init__(self, defaults=defaults) ConfigHelperMixin.__init__(self, templatize_values)
if cs: ConfigParser.RawConfigParser.__init__(self)
# Make option names case sensitive StringiferMixin.__init__(self)
# See: http://docs.python.org/library/configparser.html#ConfigParser.RawConfigParser.optionxform # Make option names case sensitive
self.optionxform = str # See: http://docs.python.org/library/configparser.html#ConfigParser.RawConfigParser.optionxform
self.optionxform = str
if fns: if fns:
for f in fns: for f in fns:
self.read(f) self.read(f)
class RewritableConfigParser(IgnoreMissingMixin, iniparse.RawConfigParser, StringiferMixin): class RewritableConfigParser(ConfigHelperMixin, iniparse.RawConfigParser, StringiferMixin):
def __init__(self, cs=True, fns=None, defaults=None): def __init__(self, fns=None, templatize_values=False):
iniparse.RawConfigParser.__init__(self, defaults=defaults) ConfigHelperMixin.__init__(self, templatize_values)
if cs: iniparse.RawConfigParser.__init__(self)
# Make option names case sensitive StringiferMixin.__init__(self)
# See: http://docs.python.org/library/configparser.html#ConfigParser.RawConfigParser.optionxform # Make option names case sensitive
self.optionxform = str # See: http://docs.python.org/library/configparser.html#ConfigParser.RawConfigParser.optionxform
self.optionxform = str
if fns: if fns:
for f in fns: for f in fns:
self.read(f) self.read(f)
@ -226,3 +242,12 @@ class YamlInterpolator(object):
self.interpolated[root] = self.included[root] self.interpolated[root] = self.included[root]
self.interpolated[root] = self._interpolate(self.interpolated[root]) self.interpolated[root] = self._interpolate(self.interpolated[root])
return 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): def _config_adjust_registry(self, contents, fn):
params = ghelper.get_shared_params(**self.options) params = ghelper.get_shared_params(**self.options)
with io.BytesIO(contents) as stream: with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser() config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream) config.readfp(stream)
config.set('DEFAULT', 'debug', self.get_bool_option('verbose')) config.set('DEFAULT', 'debug', self.get_bool_option('verbose'))
config.set('DEFAULT', 'verbose', 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'), **utils.merge_dicts(self.get_option('keystone'),
khelper.get_shared_passwords(self))) khelper.get_shared_passwords(self)))
with io.BytesIO(contents) as stream: with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser() config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream) config.readfp(stream)
config.set('filter:authtoken', 'auth_host', params['endpoints']['admin']['host']) config.set('filter:authtoken', 'auth_host', params['endpoints']['admin']['host'])
config.set('filter:authtoken', 'auth_port', params['endpoints']['admin']['port']) 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): def _config_adjust_api(self, contents, fn):
params = ghelper.get_shared_params(**self.options) params = ghelper.get_shared_params(**self.options)
with io.BytesIO(contents) as stream: with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser() config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream) config.readfp(stream)
img_store_dir = sh.joinpths(self.get_option('component_dir'), 'images') img_store_dir = sh.joinpths(self.get_option('component_dir'), 'images')
config.set('DEFAULT', 'debug', self.get_bool_option('verbose',)) config.set('DEFAULT', 'debug', self.get_bool_option('verbose',))
@ -172,7 +172,7 @@ class GlanceInstaller(comp.PythonInstallComponent):
def _config_adjust_logging(self, contents, fn): def _config_adjust_logging(self, contents, fn):
with io.BytesIO(contents) as stream: with io.BytesIO(contents) as stream:
config = cfg.RewritableConfigParser() config = cfg.create_parser(cfg.RewritableConfigParser, self)
config.readfp(stream) config.readfp(stream)
config.set('logger_root', 'level', 'DEBUG') config.set('logger_root', 'level', 'DEBUG')
config.set('logger_root', 'handlers', "devel,production") config.set('logger_root', 'handlers', "devel,production")

View File

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

View File

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

View File

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

View File

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