blob: b9d9649e4b6422704c6e731efa391ef51771a10a [file] [log] [blame]
Sean Daguee263c822014-12-05 14:25:28 -05001#!/bin/bash
2#
Dean Troyerbf2ad702015-03-09 15:16:10 -05003# **lib/meta-config** - Configuration file manipulation functions
4#
5# Support for DevStack's local.conf meta-config sections
6#
Dean Troyer893e6632013-09-13 15:05:51 -05007# 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 Dague537d4022013-10-22 07:43:22 -040017# at a particular time. These are called phases in ``stack.sh`` but
Dean Troyer893e6632013-09-13 15:05:51 -050018# 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 Wienand523f4882015-10-13 11:03:03 +110023_XTRACE_INC_META=$(set +o | grep xtrace)
Dean Troyer893e6632013-09-13 15:05:51 -050024set +o xtrace
25
26
27# Allow the awk command to be overridden on legacy platforms
28CONFIG_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 Wienandaee18c72014-02-21 15:35:08 +110032function get_meta_section {
Dean Troyer893e6632013-09-13 15:05:51 -050033 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 Yamahatabff00142013-12-20 11:55:08 +090042 /^\[\[.+\|.*\]\]/ {
YAMAMOTO Takashi02f3f9a2016-11-26 00:43:07 +090043 gsub("[][]", "", $1);
44 split($1, a, "|");
45 if (a[1] == matchgroup && a[2] == configfile) {
46 group=a[1]
Dean Troyer893e6632013-09-13 15:05:51 -050047 } 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 Wienandaee18c72014-02-21 15:35:08 +110062function get_meta_section_files {
Dean Troyer893e6632013-09-13 15:05:51 -050063 local file=$1
64 local matchgroup=$2
65
66 [[ -r $file ]] || return 0
67
68 $CONFIG_AWK_CMD -v matchgroup=$matchgroup '
Sean Dague537d4022013-10-22 07:43:22 -040069 /^\[\[.+\|.*\]\]/ {
70 gsub("[][]", "", $1);
71 split($1, a, "|");
72 if (a[1] == matchgroup)
73 print a[2]
74 }
Dean Troyer893e6632013-09-13 15:05:51 -050075 ' $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 Wienandaee18c72014-02-21 15:35:08 +110082function merge_config_file {
Dean Troyer893e6632013-09-13 15:05:51 -050083 local file=$1
84 local matchgroup=$2
85 local configfile=$3
86
Ian Wienandfa3e8412015-04-17 11:53:40 +100087 # 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 Morin85f42f62015-09-01 10:33:10 +020090 local real_configfile
91 real_configfile=$(eval echo $configfile)
Ian Wienandfa3e8412015-04-17 11:53:40 +100092 if [ ! -f $real_configfile ]; then
Dmitry Tantsure825ba02024-06-26 18:10:43 +020093 mkdir -p $(dirname $real_configfile) || die $LINENO "could not create the directory of $real_configfile ($configfile)"
Thomas Morin85f42f62015-09-01 10:33:10 +020094 touch $real_configfile || die $LINENO "could not create config file $real_configfile ($configfile)"
Ian Wienandfa3e8412015-04-17 11:53:40 +100095 fi
96
Dean Troyer893e6632013-09-13 15:05:51 -050097 get_meta_section $file $matchgroup $configfile | \
98 $CONFIG_AWK_CMD -v configfile=$configfile '
Robert Li751ad1a2014-10-15 21:40:53 -040099 BEGIN {
100 section = ""
101 last_section = ""
102 section_count = 0
103 }
Dean Troyer893e6632013-09-13 15:05:51 -0500104 /^\[.+\]/ {
105 gsub("[][]", "", $1);
106 section=$1
107 next
108 }
109 /^ *\#/ {
110 next
111 }
Dean Troyer2ac8b3f2013-12-04 17:20:28 -0600112 /^[^ \t]+/ {
Fergal Mc Carthycc87c282014-10-09 16:16:42 -0400113 # get offset of first '=' in $0
114 eq_idx = index($0, "=")
115 # extract attr & value from $0
116 attr = substr($0, 1, eq_idx - 1)
117 value = substr($0, eq_idx + 1)
118 # only need to strip trailing whitespace from attr
119 sub(/[ \t]*$/, "", attr)
120 # need to strip leading & trailing whitespace from value
121 sub(/^[ \t]*/, "", value)
122 sub(/[ \t]*$/, "", value)
Robert Li751ad1a2014-10-15 21:40:53 -0400123
124 # cfg_attr_count: number of config lines per [section, attr]
125 # cfg_attr: three dimensional array to keep all the config lines per [section, attr]
126 # cfg_section: keep the section names in the same order as they appear in local.conf
127 # cfg_sec_attr_name: keep the attr names in the same order as they appear in local.conf
128 if (! (section, attr) in cfg_attr_count) {
129 if (section != last_section) {
130 cfg_section[section_count++] = section
131 last_section = section
132 }
133 attr_count = cfg_sec_attr_count[section_count - 1]++
134 cfg_sec_attr_name[section_count - 1, attr_count] = attr
135
136 cfg_attr[section, attr, 0] = value
137 cfg_attr_count[section, attr] = 1
138 } else {
139 lno = cfg_attr_count[section, attr]++
140 cfg_attr[section, attr, lno] = value
141 }
142 }
143 END {
144 # Process each section in order
145 for (sno = 0; sno < section_count; sno++) {
146 section = cfg_section[sno]
147 # The ini routines simply append a config item immediately
148 # after the section header. To keep the same order as defined
149 # in local.conf, invoke the ini routines in the reverse order
150 for (attr_no = cfg_sec_attr_count[sno] - 1; attr_no >=0; attr_no--) {
151 attr = cfg_sec_attr_name[sno, attr_no]
152 if (cfg_attr_count[section, attr] == 1)
Ian Wienandf3bf8b62014-10-29 21:53:56 +1100153 print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, 0] "\""
Robert Li751ad1a2014-10-15 21:40:53 -0400154 else {
155 # For multiline, invoke the ini routines in the reverse order
156 count = cfg_attr_count[section, attr]
Doug Wiegley1f65fd62014-12-13 11:56:16 -0700157 print "inidelete " configfile " " section " " attr
Ian Wienandf3bf8b62014-10-29 21:53:56 +1100158 print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, count - 1] "\""
Robert Li751ad1a2014-10-15 21:40:53 -0400159 for (l = count -2; l >= 0; l--)
Ian Wienandf3bf8b62014-10-29 21:53:56 +1100160 print "iniadd_literal " configfile " " section " " attr " \"" cfg_attr[section, attr, l] "\""
Robert Li751ad1a2014-10-15 21:40:53 -0400161 }
162 }
163 }
Dean Troyer893e6632013-09-13 15:05:51 -0500164 }
165 ' | while read a; do eval "$a"; done
Dean Troyer893e6632013-09-13 15:05:51 -0500166}
167
168
169# Merge all of the files specified by group
170# merge_config_group infile group [group ...]
Ian Wienandaee18c72014-02-21 15:35:08 +1100171function merge_config_group {
Dean Troyer893e6632013-09-13 15:05:51 -0500172 local localfile=$1; shift
173 local matchgroups=$@
174
175 [[ -r $localfile ]] || return 0
176
Dean Troyerb1e3d0f2014-07-25 14:57:54 -0500177 local configfile group
Dean Troyer893e6632013-09-13 15:05:51 -0500178 for group in $matchgroups; do
179 for configfile in $(get_meta_section_files $localfile $group); do
Thomas Morin85f42f62015-09-01 10:33:10 +0200180 local realconfigfile
181 local dir
182
183 realconfigfile=$(eval "echo $configfile")
184 if [[ -z $realconfigfile ]]; then
Sean Dague22b63662016-12-09 07:33:01 -0500185 warn $LINENO "unknown config file specification: $configfile is undefined"
186 break
Thomas Morin85f42f62015-09-01 10:33:10 +0200187 fi
188 dir=$(dirname $realconfigfile)
189 if [[ -d $dir ]]; then
Dean Troyer893e6632013-09-13 15:05:51 -0500190 merge_config_file $localfile $group $configfile
Thomas Morin85f42f62015-09-01 10:33:10 +0200191 else
192 die $LINENO "bogus config file specification $configfile ($configfile=$realconfigfile, $dir is not a directory)"
Dean Troyer893e6632013-09-13 15:05:51 -0500193 fi
194 done
195 done
196}
197
Huan Xiecc6af3f2015-12-23 02:17:01 +0000198function extract_localrc_section {
199 local configfile=$1 # top_dir/local.conf
200 local localrcfile=$2 # top_dir/localrc
201 local localautofile=$3 # top_dir/.localrc.auto
202
203 if [[ -r $configfile ]]; then
204 LRC=$(get_meta_section_files $configfile local)
205 for lfile in $LRC; do
206 if [[ "$lfile" == "localrc" ]]; then
207 if [[ -r $localrcfile ]]; then
208 echo "localrc and local.conf:[[local]] both exist, using localrc"
209 else
210 echo "# Generated file, do not edit" >$localautofile
211 get_meta_section $configfile local $lfile >>$localautofile
212 fi
213 fi
214 done
215 fi
216}
Dean Troyer893e6632013-09-13 15:05:51 -0500217
218# Restore xtrace
Ian Wienand523f4882015-10-13 11:03:03 +1100219$_XTRACE_INC_META
Dean Troyer893e6632013-09-13 15:05:51 -0500220
221# Local variables:
222# mode: shell-script
223# End: