Sean Dague | e263c82 | 2014-12-05 14:25:28 -0500 | [diff] [blame] | 1 | #!/bin/bash |
| 2 | # |
Dean Troyer | bf2ad70 | 2015-03-09 15:16:10 -0500 | [diff] [blame] | 3 | # **lib/meta-config** - Configuration file manipulation functions |
| 4 | # |
| 5 | # Support for DevStack's local.conf meta-config sections |
| 6 | # |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 7 | # These functions have no external dependencies and the following side-effects: |
| 8 | # |
| 9 | # CONFIG_AWK_CMD is defined, default is ``awk`` |
| 10 | |
| 11 | # Meta-config files contain multiple INI-style configuration files |
| 12 | # using a specific new section header to delimit them: |
| 13 | # |
| 14 | # [[group-name|file-name]] |
| 15 | # |
| 16 | # group-name refers to the group of configuration file changes to be processed |
Sean Dague | 537d402 | 2013-10-22 07:43:22 -0400 | [diff] [blame] | 17 | # at a particular time. These are called phases in ``stack.sh`` but |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 18 | # group here as these functions are not DevStack-specific. |
| 19 | # |
| 20 | # file-name is the destination of the config file |
| 21 | |
| 22 | # Save trace setting |
Ian Wienand | 523f488 | 2015-10-13 11:03:03 +1100 | [diff] [blame] | 23 | _XTRACE_INC_META=$(set +o | grep xtrace) |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 24 | set +o xtrace |
| 25 | |
| 26 | |
| 27 | # Allow the awk command to be overridden on legacy platforms |
| 28 | CONFIG_AWK_CMD=${CONFIG_AWK_CMD:-awk} |
| 29 | |
| 30 | # Get the section for the specific group and config file |
| 31 | # get_meta_section infile group configfile |
Ian Wienand | aee18c7 | 2014-02-21 15:35:08 +1100 | [diff] [blame] | 32 | function get_meta_section { |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 33 | local file=$1 |
| 34 | local matchgroup=$2 |
| 35 | local configfile=$3 |
| 36 | |
| 37 | [[ -r $file ]] || return 0 |
| 38 | [[ -z $configfile ]] && return 0 |
| 39 | |
| 40 | $CONFIG_AWK_CMD -v matchgroup=$matchgroup -v configfile=$configfile ' |
| 41 | BEGIN { group = "" } |
Isaku Yamahata | bff0014 | 2013-12-20 11:55:08 +0900 | [diff] [blame] | 42 | /^\[\[.+\|.*\]\]/ { |
YAMAMOTO Takashi | 02f3f9a | 2016-11-26 00:43:07 +0900 | [diff] [blame^] | 43 | gsub("[][]", "", $1); |
| 44 | split($1, a, "|"); |
| 45 | if (a[1] == matchgroup && a[2] == configfile) { |
| 46 | group=a[1] |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 47 | } else { |
| 48 | group="" |
| 49 | } |
| 50 | next |
| 51 | } |
| 52 | { |
| 53 | if (group != "") |
| 54 | print $0 |
| 55 | } |
| 56 | ' $file |
| 57 | } |
| 58 | |
| 59 | |
| 60 | # Get a list of config files for a specific group |
| 61 | # get_meta_section_files infile group |
Ian Wienand | aee18c7 | 2014-02-21 15:35:08 +1100 | [diff] [blame] | 62 | function get_meta_section_files { |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 63 | local file=$1 |
| 64 | local matchgroup=$2 |
| 65 | |
| 66 | [[ -r $file ]] || return 0 |
| 67 | |
| 68 | $CONFIG_AWK_CMD -v matchgroup=$matchgroup ' |
Sean Dague | 537d402 | 2013-10-22 07:43:22 -0400 | [diff] [blame] | 69 | /^\[\[.+\|.*\]\]/ { |
| 70 | gsub("[][]", "", $1); |
| 71 | split($1, a, "|"); |
| 72 | if (a[1] == matchgroup) |
| 73 | print a[2] |
| 74 | } |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 75 | ' $file |
| 76 | } |
| 77 | |
| 78 | |
| 79 | # Merge the contents of a meta-config file into its destination config file |
| 80 | # If configfile does not exist it will be created. |
| 81 | # merge_config_file infile group configfile |
Ian Wienand | aee18c7 | 2014-02-21 15:35:08 +1100 | [diff] [blame] | 82 | function merge_config_file { |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 83 | local file=$1 |
| 84 | local matchgroup=$2 |
| 85 | local configfile=$3 |
| 86 | |
Ian Wienand | fa3e841 | 2015-04-17 11:53:40 +1000 | [diff] [blame] | 87 | # note, configfile might be a variable (note the iniset, etc |
| 88 | # created in the mega-awk below is "eval"ed too, so we just leave |
| 89 | # it alone. |
Thomas Morin | 85f42f6 | 2015-09-01 10:33:10 +0200 | [diff] [blame] | 90 | local real_configfile |
| 91 | real_configfile=$(eval echo $configfile) |
Ian Wienand | fa3e841 | 2015-04-17 11:53:40 +1000 | [diff] [blame] | 92 | if [ ! -f $real_configfile ]; then |
Thomas Morin | 85f42f6 | 2015-09-01 10:33:10 +0200 | [diff] [blame] | 93 | touch $real_configfile || die $LINENO "could not create config file $real_configfile ($configfile)" |
Ian Wienand | fa3e841 | 2015-04-17 11:53:40 +1000 | [diff] [blame] | 94 | fi |
| 95 | |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 96 | get_meta_section $file $matchgroup $configfile | \ |
| 97 | $CONFIG_AWK_CMD -v configfile=$configfile ' |
Robert Li | 751ad1a | 2014-10-15 21:40:53 -0400 | [diff] [blame] | 98 | BEGIN { |
| 99 | section = "" |
| 100 | last_section = "" |
| 101 | section_count = 0 |
| 102 | } |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 103 | /^\[.+\]/ { |
| 104 | gsub("[][]", "", $1); |
| 105 | section=$1 |
| 106 | next |
| 107 | } |
| 108 | /^ *\#/ { |
| 109 | next |
| 110 | } |
Dean Troyer | 2ac8b3f | 2013-12-04 17:20:28 -0600 | [diff] [blame] | 111 | /^[^ \t]+/ { |
Fergal Mc Carthy | cc87c28 | 2014-10-09 16:16:42 -0400 | [diff] [blame] | 112 | # get offset of first '=' in $0 |
| 113 | eq_idx = index($0, "=") |
| 114 | # extract attr & value from $0 |
| 115 | attr = substr($0, 1, eq_idx - 1) |
| 116 | value = substr($0, eq_idx + 1) |
| 117 | # only need to strip trailing whitespace from attr |
| 118 | sub(/[ \t]*$/, "", attr) |
| 119 | # need to strip leading & trailing whitespace from value |
| 120 | sub(/^[ \t]*/, "", value) |
| 121 | sub(/[ \t]*$/, "", value) |
Robert Li | 751ad1a | 2014-10-15 21:40:53 -0400 | [diff] [blame] | 122 | |
| 123 | # cfg_attr_count: number of config lines per [section, attr] |
| 124 | # cfg_attr: three dimensional array to keep all the config lines per [section, attr] |
| 125 | # cfg_section: keep the section names in the same order as they appear in local.conf |
| 126 | # cfg_sec_attr_name: keep the attr names in the same order as they appear in local.conf |
| 127 | if (! (section, attr) in cfg_attr_count) { |
| 128 | if (section != last_section) { |
| 129 | cfg_section[section_count++] = section |
| 130 | last_section = section |
| 131 | } |
| 132 | attr_count = cfg_sec_attr_count[section_count - 1]++ |
| 133 | cfg_sec_attr_name[section_count - 1, attr_count] = attr |
| 134 | |
| 135 | cfg_attr[section, attr, 0] = value |
| 136 | cfg_attr_count[section, attr] = 1 |
| 137 | } else { |
| 138 | lno = cfg_attr_count[section, attr]++ |
| 139 | cfg_attr[section, attr, lno] = value |
| 140 | } |
| 141 | } |
| 142 | END { |
| 143 | # Process each section in order |
| 144 | for (sno = 0; sno < section_count; sno++) { |
| 145 | section = cfg_section[sno] |
| 146 | # The ini routines simply append a config item immediately |
| 147 | # after the section header. To keep the same order as defined |
| 148 | # in local.conf, invoke the ini routines in the reverse order |
| 149 | for (attr_no = cfg_sec_attr_count[sno] - 1; attr_no >=0; attr_no--) { |
| 150 | attr = cfg_sec_attr_name[sno, attr_no] |
| 151 | if (cfg_attr_count[section, attr] == 1) |
Ian Wienand | f3bf8b6 | 2014-10-29 21:53:56 +1100 | [diff] [blame] | 152 | print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, 0] "\"" |
Robert Li | 751ad1a | 2014-10-15 21:40:53 -0400 | [diff] [blame] | 153 | else { |
| 154 | # For multiline, invoke the ini routines in the reverse order |
| 155 | count = cfg_attr_count[section, attr] |
Doug Wiegley | 1f65fd6 | 2014-12-13 11:56:16 -0700 | [diff] [blame] | 156 | print "inidelete " configfile " " section " " attr |
Ian Wienand | f3bf8b6 | 2014-10-29 21:53:56 +1100 | [diff] [blame] | 157 | print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, count - 1] "\"" |
Robert Li | 751ad1a | 2014-10-15 21:40:53 -0400 | [diff] [blame] | 158 | for (l = count -2; l >= 0; l--) |
Ian Wienand | f3bf8b6 | 2014-10-29 21:53:56 +1100 | [diff] [blame] | 159 | print "iniadd_literal " configfile " " section " " attr " \"" cfg_attr[section, attr, l] "\"" |
Robert Li | 751ad1a | 2014-10-15 21:40:53 -0400 | [diff] [blame] | 160 | } |
| 161 | } |
| 162 | } |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 163 | } |
| 164 | ' | while read a; do eval "$a"; done |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 165 | } |
| 166 | |
| 167 | |
| 168 | # Merge all of the files specified by group |
| 169 | # merge_config_group infile group [group ...] |
Ian Wienand | aee18c7 | 2014-02-21 15:35:08 +1100 | [diff] [blame] | 170 | function merge_config_group { |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 171 | local localfile=$1; shift |
| 172 | local matchgroups=$@ |
| 173 | |
| 174 | [[ -r $localfile ]] || return 0 |
| 175 | |
Dean Troyer | b1e3d0f | 2014-07-25 14:57:54 -0500 | [diff] [blame] | 176 | local configfile group |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 177 | for group in $matchgroups; do |
| 178 | for configfile in $(get_meta_section_files $localfile $group); do |
Thomas Morin | 85f42f6 | 2015-09-01 10:33:10 +0200 | [diff] [blame] | 179 | local realconfigfile |
| 180 | local dir |
| 181 | |
| 182 | realconfigfile=$(eval "echo $configfile") |
| 183 | if [[ -z $realconfigfile ]]; then |
| 184 | die $LINENO "bogus config file specification: $configfile is undefined" |
| 185 | fi |
| 186 | dir=$(dirname $realconfigfile) |
| 187 | if [[ -d $dir ]]; then |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 188 | merge_config_file $localfile $group $configfile |
Thomas Morin | 85f42f6 | 2015-09-01 10:33:10 +0200 | [diff] [blame] | 189 | else |
| 190 | die $LINENO "bogus config file specification $configfile ($configfile=$realconfigfile, $dir is not a directory)" |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 191 | fi |
| 192 | done |
| 193 | done |
| 194 | } |
| 195 | |
Huan Xie | cc6af3f | 2015-12-23 02:17:01 +0000 | [diff] [blame] | 196 | function extract_localrc_section { |
| 197 | local configfile=$1 # top_dir/local.conf |
| 198 | local localrcfile=$2 # top_dir/localrc |
| 199 | local localautofile=$3 # top_dir/.localrc.auto |
| 200 | |
| 201 | if [[ -r $configfile ]]; then |
| 202 | LRC=$(get_meta_section_files $configfile local) |
| 203 | for lfile in $LRC; do |
| 204 | if [[ "$lfile" == "localrc" ]]; then |
| 205 | if [[ -r $localrcfile ]]; then |
| 206 | echo "localrc and local.conf:[[local]] both exist, using localrc" |
| 207 | else |
| 208 | echo "# Generated file, do not edit" >$localautofile |
| 209 | get_meta_section $configfile local $lfile >>$localautofile |
| 210 | fi |
| 211 | fi |
| 212 | done |
| 213 | fi |
| 214 | } |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 215 | |
| 216 | # Restore xtrace |
Ian Wienand | 523f488 | 2015-10-13 11:03:03 +1100 | [diff] [blame] | 217 | $_XTRACE_INC_META |
Dean Troyer | 893e663 | 2013-09-13 15:05:51 -0500 | [diff] [blame] | 218 | |
| 219 | # Local variables: |
| 220 | # mode: shell-script |
| 221 | # End: |