blob: b9ab6b207f6e2842f59f083dd8cd5a61066e27d5 [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
Dean Troyerbf2ad702015-03-09 15:16:10 -050023INC_META_XTRACE=$(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 /^\[\[.+\|.*\]\]/ {
Dean Troyer893e6632013-09-13 15:05:51 -050043 if (group == "") {
44 gsub("[][]", "", $1);
45 split($1, a, "|");
46 if (a[1] == matchgroup && a[2] == configfile) {
47 group=a[1]
48 }
49 } else {
50 group=""
51 }
52 next
53 }
54 {
55 if (group != "")
56 print $0
57 }
58 ' $file
59}
60
61
62# Get a list of config files for a specific group
63# get_meta_section_files infile group
Ian Wienandaee18c72014-02-21 15:35:08 +110064function get_meta_section_files {
Dean Troyer893e6632013-09-13 15:05:51 -050065 local file=$1
66 local matchgroup=$2
67
68 [[ -r $file ]] || return 0
69
70 $CONFIG_AWK_CMD -v matchgroup=$matchgroup '
Sean Dague537d4022013-10-22 07:43:22 -040071 /^\[\[.+\|.*\]\]/ {
72 gsub("[][]", "", $1);
73 split($1, a, "|");
74 if (a[1] == matchgroup)
75 print a[2]
76 }
Dean Troyer893e6632013-09-13 15:05:51 -050077 ' $file
78}
79
80
81# Merge the contents of a meta-config file into its destination config file
82# If configfile does not exist it will be created.
83# merge_config_file infile group configfile
Ian Wienandaee18c72014-02-21 15:35:08 +110084function merge_config_file {
Dean Troyer893e6632013-09-13 15:05:51 -050085 local file=$1
86 local matchgroup=$2
87 local configfile=$3
88
Ian Wienandfa3e8412015-04-17 11:53:40 +100089 # note, configfile might be a variable (note the iniset, etc
90 # created in the mega-awk below is "eval"ed too, so we just leave
91 # it alone.
Thomas Morin85f42f62015-09-01 10:33:10 +020092 local real_configfile
93 real_configfile=$(eval echo $configfile)
Ian Wienandfa3e8412015-04-17 11:53:40 +100094 if [ ! -f $real_configfile ]; then
Thomas Morin85f42f62015-09-01 10:33:10 +020095 touch $real_configfile || die $LINENO "could not create config file $real_configfile ($configfile)"
Ian Wienandfa3e8412015-04-17 11:53:40 +100096 fi
97
Dean Troyer893e6632013-09-13 15:05:51 -050098 get_meta_section $file $matchgroup $configfile | \
99 $CONFIG_AWK_CMD -v configfile=$configfile '
Robert Li751ad1a2014-10-15 21:40:53 -0400100 BEGIN {
101 section = ""
102 last_section = ""
103 section_count = 0
104 }
Dean Troyer893e6632013-09-13 15:05:51 -0500105 /^\[.+\]/ {
106 gsub("[][]", "", $1);
107 section=$1
108 next
109 }
110 /^ *\#/ {
111 next
112 }
Dean Troyer2ac8b3f2013-12-04 17:20:28 -0600113 /^[^ \t]+/ {
Fergal Mc Carthycc87c282014-10-09 16:16:42 -0400114 # get offset of first '=' in $0
115 eq_idx = index($0, "=")
116 # extract attr & value from $0
117 attr = substr($0, 1, eq_idx - 1)
118 value = substr($0, eq_idx + 1)
119 # only need to strip trailing whitespace from attr
120 sub(/[ \t]*$/, "", attr)
121 # need to strip leading & trailing whitespace from value
122 sub(/^[ \t]*/, "", value)
123 sub(/[ \t]*$/, "", value)
Robert Li751ad1a2014-10-15 21:40:53 -0400124
125 # cfg_attr_count: number of config lines per [section, attr]
126 # cfg_attr: three dimensional array to keep all the config lines per [section, attr]
127 # cfg_section: keep the section names in the same order as they appear in local.conf
128 # cfg_sec_attr_name: keep the attr names in the same order as they appear in local.conf
129 if (! (section, attr) in cfg_attr_count) {
130 if (section != last_section) {
131 cfg_section[section_count++] = section
132 last_section = section
133 }
134 attr_count = cfg_sec_attr_count[section_count - 1]++
135 cfg_sec_attr_name[section_count - 1, attr_count] = attr
136
137 cfg_attr[section, attr, 0] = value
138 cfg_attr_count[section, attr] = 1
139 } else {
140 lno = cfg_attr_count[section, attr]++
141 cfg_attr[section, attr, lno] = value
142 }
143 }
144 END {
145 # Process each section in order
146 for (sno = 0; sno < section_count; sno++) {
147 section = cfg_section[sno]
148 # The ini routines simply append a config item immediately
149 # after the section header. To keep the same order as defined
150 # in local.conf, invoke the ini routines in the reverse order
151 for (attr_no = cfg_sec_attr_count[sno] - 1; attr_no >=0; attr_no--) {
152 attr = cfg_sec_attr_name[sno, attr_no]
153 if (cfg_attr_count[section, attr] == 1)
Ian Wienandf3bf8b62014-10-29 21:53:56 +1100154 print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, 0] "\""
Robert Li751ad1a2014-10-15 21:40:53 -0400155 else {
156 # For multiline, invoke the ini routines in the reverse order
157 count = cfg_attr_count[section, attr]
Doug Wiegley1f65fd62014-12-13 11:56:16 -0700158 print "inidelete " configfile " " section " " attr
Ian Wienandf3bf8b62014-10-29 21:53:56 +1100159 print "iniset " configfile " " section " " attr " \"" cfg_attr[section, attr, count - 1] "\""
Robert Li751ad1a2014-10-15 21:40:53 -0400160 for (l = count -2; l >= 0; l--)
Ian Wienandf3bf8b62014-10-29 21:53:56 +1100161 print "iniadd_literal " configfile " " section " " attr " \"" cfg_attr[section, attr, l] "\""
Robert Li751ad1a2014-10-15 21:40:53 -0400162 }
163 }
164 }
Dean Troyer893e6632013-09-13 15:05:51 -0500165 }
166 ' | while read a; do eval "$a"; done
Dean Troyer893e6632013-09-13 15:05:51 -0500167}
168
169
170# Merge all of the files specified by group
171# merge_config_group infile group [group ...]
Ian Wienandaee18c72014-02-21 15:35:08 +1100172function merge_config_group {
Dean Troyer893e6632013-09-13 15:05:51 -0500173 local localfile=$1; shift
174 local matchgroups=$@
175
176 [[ -r $localfile ]] || return 0
177
Dean Troyerb1e3d0f2014-07-25 14:57:54 -0500178 local configfile group
Dean Troyer893e6632013-09-13 15:05:51 -0500179 for group in $matchgroups; do
180 for configfile in $(get_meta_section_files $localfile $group); do
Thomas Morin85f42f62015-09-01 10:33:10 +0200181 local realconfigfile
182 local dir
183
184 realconfigfile=$(eval "echo $configfile")
185 if [[ -z $realconfigfile ]]; then
186 die $LINENO "bogus config file specification: $configfile is undefined"
187 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
198
199# Restore xtrace
Dean Troyerbf2ad702015-03-09 15:16:10 -0500200$INC_META_XTRACE
Dean Troyer893e6632013-09-13 15:05:51 -0500201
202# Local variables:
203# mode: shell-script
204# End: