
Currently our gather playbook assumes all configuration files will be in /etc. This assumption isn't true with container deployments. Currently the configuration files are located in /var/lib/config-data/<service>/etc/<service>/. The Gather script needs to support both container and non-container deployments. This patchset updates the config parser python script to check if a service is in the running containers list and then determine it's appropriate path, grab all of multiple config files in that path, then parse and drop them off for ansible to use. This method automagically supports all possible mixes of containerized uncontainerized services and will always grab the correct config even if that changes build to build or run to run. It's also easily extensible for many possible config locations or different container types by adding another condition or additional search paths. Change-Id: I95a3059c7fc263733ac64aa894c6dbf11e2a909f Closes-bug: #1701264
155 lines
5.4 KiB
Python
155 lines
5.4 KiB
Python
#!/usr/bin/env python
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import sys
|
|
import os
|
|
import subprocess
|
|
# usage: openstack-config-parser.py [service] [output file]
|
|
|
|
def run_cmd(cmd):
|
|
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
stdout, stderr = process.communicate()
|
|
output_dict = {}
|
|
output_dict['stdout'] = stdout.strip()
|
|
output_dict['stderr'] = stderr.strip()
|
|
output_dict['rc'] = process.returncode
|
|
return output_dict
|
|
|
|
def strip_chars(line):
|
|
forbidden_chars = ['#', '\n', '"', '\\', ' ', '<', '>']
|
|
for char in forbidden_chars:
|
|
line = line.replace(char, '')
|
|
return line
|
|
|
|
def parse_config(serviceName, fileName):
|
|
# a dict containing key/value pairs, last value is what is stored.
|
|
values = {}
|
|
with open(fileName) as config:
|
|
section = None
|
|
for line in config:
|
|
pair = strip_chars(line)
|
|
pair = pair.split('=')
|
|
# excludes any line without a key/val pair
|
|
valid_line = not line.startswith("# ") and \
|
|
'[' not in line and line != '\n' \
|
|
and line != '#\n' and "password" \
|
|
not in line.lower()
|
|
if line.startswith('['):
|
|
section = line.replace('[','').replace(']','').replace('\n','')
|
|
if '#' not in line and valid_line and not section == None and len(pair) == 2:
|
|
pair[0] = strip_chars(pair[0])
|
|
pair[1] = strip_chars(pair[1])
|
|
values["openstack_S_" + serviceName + "_S_" + section + "_S_" + pair[0]] = pair[1]
|
|
return values
|
|
|
|
|
|
def try_type(val):
|
|
try:
|
|
int(val)
|
|
return val
|
|
except (ValueError, TypeError):
|
|
try:
|
|
float(val)
|
|
return val
|
|
except (ValueError, TypeError):
|
|
if type(val) is list:
|
|
return "\"" + str(val) + "\""
|
|
elif val.lower() in ("true", "false"):
|
|
return val
|
|
else:
|
|
return "\"" + val + "\""
|
|
|
|
|
|
def add_conf_location(serviceName, fileName, values):
|
|
# Stores the exact location we gathered this config from.
|
|
index = "openstack_S_" + serviceName + "_S_" + "Browbeat" + "_S_" + "gather_conf_path"
|
|
if index in values:
|
|
values[index].append(fileName)
|
|
else:
|
|
values[index] = [fileName]
|
|
|
|
def print_vars_file(values, fileName):
|
|
with open(fileName, 'w') as output:
|
|
for key in values:
|
|
output.write(key + ": " + try_type(values[key]) + "\n")
|
|
|
|
def is_containerized(service_name):
|
|
out = run_cmd("docker ps")
|
|
if service_name in out['stdout']:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def get_configs_list(path, extension='.conf'):
|
|
configs = []
|
|
for item in os.listdir(path):
|
|
if item.endswith(extension):
|
|
configs.extend([item])
|
|
return configs
|
|
|
|
def get_neutron_plugin(output, cfg_path):
|
|
plugin = output['openstack_S_neutron_S_DEFAULT_S_core_plugin']
|
|
plugin_path = "{}/plugins/{}/".format(cfg_path, plugin)
|
|
for item in get_configs_list(plugin_path, extension='.ini'):
|
|
full_path = "{}/{}".format(plugin_path,
|
|
item)
|
|
output.update(parse_config("neutron-plugin", full_path))
|
|
add_conf_location("neutron", full_path, output)
|
|
return output
|
|
|
|
def main():
|
|
if len(sys.argv) < 3:
|
|
print("usage: openstack-config-parser.py [service] [output file]")
|
|
exit(1)
|
|
|
|
service_name = sys.argv[1]
|
|
outfile = sys.argv[2]
|
|
|
|
# This is a list of services that require exceptions from the usual
|
|
# pattern when gathering their config files
|
|
pattern_exceptions = ['glance']
|
|
in_container = is_containerized(service_name)
|
|
|
|
|
|
if 'undercloud' in service_name:
|
|
cfg_path = "/home/stack"
|
|
elif in_container and service_name not in pattern_exceptions:
|
|
cfg_path = "/var/lib/config-data/{}/etc/{}".format(service_name,
|
|
service_name)
|
|
# Glance has all configs in a folder named glance_api, ps shows no
|
|
# processes outside of the container, so I assume those are the right
|
|
# configs, even though the container is also named glance-api
|
|
# jkilpatr 7/13/17
|
|
elif in_container and 'glance' in service_name:
|
|
cfg_path = "/var/lib/config-data/glance_api/etc/glance"
|
|
else:
|
|
cfg_path = "/etc/{}".format(service_name)
|
|
|
|
print("Parsing all .conf files in {}".format(cfg_path))
|
|
output = {}
|
|
for item in get_configs_list(cfg_path):
|
|
full_path = "{}/{}".format(cfg_path,
|
|
item)
|
|
output.update(parse_config(service_name, full_path))
|
|
add_conf_location(service_name, full_path, output)
|
|
# Required to find and load the active neutron plugin file.
|
|
if 'neutron' in service_name:
|
|
output.update(get_neutron_plugin(output, cfg_path))
|
|
|
|
print_vars_file(output, outfile)
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|
|
|