devstack/lib/config
Robert Li 6ff21acf4c Allow multi-line config items in meta-section of local.conf
It would behave such as the contents from each meta-section in
local.conf is copied to the destination files. One exception is the multiline
options not grouped together. In that case, the contents will be grouped
together in its destination config file.

Check tests/test_config.sh for examples.

Change-Id: I8c046b558eeb98ed221f6f1a59182d4179956ced
Partial-Bug: #1374118
2014-10-10 12:53:24 -04:00

183 lines
5.9 KiB
Plaintext

# lib/config - Configuration file manipulation functions
# These functions have no external dependencies and the following side-effects:
#
# CONFIG_AWK_CMD is defined, default is ``awk``
# Meta-config files contain multiple INI-style configuration files
# using a specific new section header to delimit them:
#
# [[group-name|file-name]]
#
# group-name refers to the group of configuration file changes to be processed
# at a particular time. These are called phases in ``stack.sh`` but
# group here as these functions are not DevStack-specific.
#
# file-name is the destination of the config file
# Save trace setting
C_XTRACE=$(set +o | grep xtrace)
set +o xtrace
# Allow the awk command to be overridden on legacy platforms
CONFIG_AWK_CMD=${CONFIG_AWK_CMD:-awk}
# Get the section for the specific group and config file
# get_meta_section infile group configfile
function get_meta_section {
local file=$1
local matchgroup=$2
local configfile=$3
[[ -r $file ]] || return 0
[[ -z $configfile ]] && return 0
$CONFIG_AWK_CMD -v matchgroup=$matchgroup -v configfile=$configfile '
BEGIN { group = "" }
/^\[\[.+\|.*\]\]/ {
if (group == "") {
gsub("[][]", "", $1);
split($1, a, "|");
if (a[1] == matchgroup && a[2] == configfile) {
group=a[1]
}
} else {
group=""
}
next
}
{
if (group != "")
print $0
}
' $file
}
# Get a list of config files for a specific group
# get_meta_section_files infile group
function get_meta_section_files {
local file=$1
local matchgroup=$2
[[ -r $file ]] || return 0
$CONFIG_AWK_CMD -v matchgroup=$matchgroup '
/^\[\[.+\|.*\]\]/ {
gsub("[][]", "", $1);
split($1, a, "|");
if (a[1] == matchgroup)
print a[2]
}
' $file
}
# Merge the contents of a meta-config file into its destination config file
# If configfile does not exist it will be created.
# merge_config_file infile group configfile
function merge_config_file {
local file=$1
local matchgroup=$2
local configfile=$3
# note in the awk below, \x27 is ascii for ' -- this avoids
# having to do nasty quoting games
get_meta_section $file $matchgroup $configfile | \
$CONFIG_AWK_CMD -v configfile=$configfile '
BEGIN {
section = ""
last_section = ""
section_count = 0
}
/^\[.+\]/ {
gsub("[][]", "", $1);
section=$1
next
}
/^ *\#/ {
next
}
/^[^ \t]+/ {
# get offset of first '=' in $0
eq_idx = index($0, "=")
# extract attr & value from $0
attr = substr($0, 1, eq_idx - 1)
value = substr($0, eq_idx + 1)
# only need to strip trailing whitespace from attr
sub(/[ \t]*$/, "", attr)
# need to strip leading & trailing whitespace from value
sub(/^[ \t]*/, "", value)
sub(/[ \t]*$/, "", value)
# cfg_attr_count: number of config lines per [section, attr]
# cfg_attr: two dimensional array to keep all the config lines per [section, attr]
# cfg_section: keep the section names in the same order as they appear in local.conf
# cfg_sec_attr_name: keep the attr names in the same order as they appear in local.conf
if (! (section, attr) in cfg_attr_count) {
if (section != last_section) {
cfg_section[section_count++] = section
last_section = section
}
attr_count = cfg_sec_attr_count[section_count - 1]++
cfg_sec_attr_name[section_count - 1][attr_count] = attr
cfg_attr[section, attr][0] = value
cfg_attr_count[section, attr] = 1
} else {
lno = cfg_attr_count[section, attr]++
cfg_attr[section, attr][lno] = value
}
}
END {
# Process each section in order
for (sno = 0; sno < section_count; sno++) {
section = cfg_section[sno]
# The ini routines simply append a config item immediately
# after the section header. To keep the same order as defined
# in local.conf, invoke the ini routines in the reverse order
for (attr_no = cfg_sec_attr_count[sno] - 1; attr_no >=0; attr_no--) {
attr = cfg_sec_attr_name[sno][attr_no]
if (cfg_attr_count[section, attr] == 1)
print "iniset " configfile " " section " " attr " \x27" cfg_attr[section, attr][0] "\x27"
else {
# For multiline, invoke the ini routines in the reverse order
count = cfg_attr_count[section, attr]
print "iniset " configfile " " section " " attr " \x27" cfg_attr[section, attr][count - 1] "\x27"
for (l = count -2; l >= 0; l--)
print "iniadd_literal " configfile " " section " " attr " \x27" cfg_attr[section, attr][l] "\x27"
}
}
}
}
' | while read a; do eval "$a"; done
}
# Merge all of the files specified by group
# merge_config_group infile group [group ...]
function merge_config_group {
local localfile=$1; shift
local matchgroups=$@
[[ -r $localfile ]] || return 0
local configfile group
for group in $matchgroups; do
for configfile in $(get_meta_section_files $localfile $group); do
if [[ -d $(dirname $(eval "echo $configfile")) ]]; then
merge_config_file $localfile $group $configfile
fi
done
done
}
# Restore xtrace
$C_XTRACE
# Local variables:
# mode: shell-script
# End: