| #!/bin/bash |
| # |
| # **lib/meta-config** - Configuration file manipulation functions |
| # |
| # Support for DevStack's local.conf meta-config sections |
| # |
| # 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 |
| _XTRACE_INC_META=$(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 = "" } |
| /^\[\[.+\|.*\]\]/ { |
| 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, configfile might be a variable (note the iniset, etc |
| # created in the mega-awk below is "eval"ed too, so we just leave |
| # it alone. |
| local real_configfile |
| real_configfile=$(eval echo $configfile) |
| if [ ! -f $real_configfile ]; then |
| mkdir -p $(dirname $real_configfile) || die $LINENO "could not create the directory of $real_configfile ($configfile)" |
| touch $real_configfile || die $LINENO "could not create config file $real_configfile ($configfile)" |
| fi |
| |
| 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: three 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 " \"" cfg_attr[section, attr, 0] "\"" |
| else { |
| # For multiline, invoke the ini routines in the reverse order |
| count = cfg_attr_count[section, attr] |
| print "inidelete " configfile " " section " " attr |
| print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, count - 1] "\"" |
| for (l = count -2; l >= 0; l--) |
| print "iniadd_literal " configfile " " section " " attr " \"" cfg_attr[section, attr, l] "\"" |
| } |
| } |
| } |
| } |
| ' | 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 |
| local realconfigfile |
| local dir |
| |
| realconfigfile=$(eval "echo $configfile") |
| if [[ -z $realconfigfile ]]; then |
| warn $LINENO "unknown config file specification: $configfile is undefined" |
| break |
| fi |
| dir=$(dirname $realconfigfile) |
| if [[ -d $dir ]]; then |
| merge_config_file $localfile $group $configfile |
| else |
| die $LINENO "bogus config file specification $configfile ($configfile=$realconfigfile, $dir is not a directory)" |
| fi |
| done |
| done |
| } |
| |
| function extract_localrc_section { |
| local configfile=$1 # top_dir/local.conf |
| local localrcfile=$2 # top_dir/localrc |
| local localautofile=$3 # top_dir/.localrc.auto |
| |
| if [[ -r $configfile ]]; then |
| LRC=$(get_meta_section_files $configfile local) |
| for lfile in $LRC; do |
| if [[ "$lfile" == "localrc" ]]; then |
| if [[ -r $localrcfile ]]; then |
| echo "localrc and local.conf:[[local]] both exist, using localrc" |
| else |
| echo "# Generated file, do not edit" >$localautofile |
| get_meta_section $configfile local $lfile >>$localautofile |
| fi |
| fi |
| done |
| fi |
| } |
| |
| # Restore xtrace |
| $_XTRACE_INC_META |
| |
| # Local variables: |
| # mode: shell-script |
| # End: |