Skip to content

Instantly share code, notes, and snippets.

@utoddl
Created November 23, 2023 13:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save utoddl/216861a76496d9431db3b1cd3936679c to your computer and use it in GitHub Desktop.
Save utoddl/216861a76496d9431db3b1cd3936679c to your computer and use it in GitHub Desktop.
converts ansible.cfg settings to shell environment variables
#!/bin/env python3
import os
import re
import yaml
import ast
import pprint
import configparser
import subprocess
import argparse
import textwrap
from shlex import quote
def val2env(cfg, val, name, _from):
global args
if args.c:
ftmstr = "{}setenv {} {}"
else:
fmtstr = "{}export {}={}"
try:
if cfg['env'][-1]['name'][0] == '_':
return "# Skipping '{}' -- for internal use only.".format(name)
except:
pass
if 'default' not in cfg:
df = 'None'
else:
df = cfg['default']
try:
df = ast.literal_eval(df)
except:
try:
df = ast.literal_eval("'" + df + "'")
except:
df = cfg['default']
if _from == 'default':
vj = df
else:
try:
vj = ast.literal_eval(val)
except:
try:
vj = ast.literal_eval("'" + val + "'")
except:
vj = val
if "{}".format(vj) == "{}".format(df):
print("# Skipping '{}' because specified value is the same as the default.".format(name))
skp = '# '
else:
skp = ''
if 'env' not in cfg:
return "# No 'env' equivalent; skipping."
if len(cfg['env']) == 0:
return "# No 'env' equivalent; skipping."
if isinstance(cfg['env'],list) and len(cfg['env']) > 0 and 'name' in cfg['env'][-1]:
cfg_env_name = cfg['env'][-1]['name']
else:
cfg_env_name = '_missing_'
iterable=False
try:
iter(vj)
if not isinstance(vj, str):
iterable = True
except:
pass
if vj == None:
return fmtstr.format(skp, cfg_env_name, 'None')
elif 'type' not in cfg:
try:
qvj = quote(vj)
return fmtstr.format(skp, cfg_env_name, quote(vj))
except:
return fmtstr.format(skp, cfg_env_name, vj)
elif cfg['type'] == 'pathspec':
return fmtstr.format(skp, cfg_env_name, quote(':'.join(vj)) if iterable else quote("{}".format(vj)))
elif cfg['type'] in ['bool', 'boolean']:
return fmtstr.format(skp, cfg_env_name, 'None' if vj == None else 'True' if vj else 'False')
elif cfg['type'] in ['list', 'pathlist']:
return fmtstr.format(skp, cfg_env_name, quote(','.join(vj)) if iterable else quote("{}".format(vj)))
elif cfg['type'] in ['dict']:
return fmtstr.format(skp, cfg_env_name, quote("{}".format(vj)))
else:
return fmtstr.format(skp, cfg_env_name, quote("{}".format(vj)))
def main():
global args
parser = argparse.ArgumentParser(epilog=textwrap.dedent('''\
ansible-cfg2env converts ansible configuration settings from ansible.cfg
format to a format suitable to set the equivalent command shell environment
variables. Given that Ansible ignores system level ansible.cfg files after
finding a user's ~/.ansible.cfg (rather than combining settings from all
found config files), we're now recommending that users use environment
variables to override only those settings they need to change. Settings
from environment variables take precedence over those from found
ansible.cfg files.
Typical usage:
ansible-cfg2env -a > ~/.ansible.cfg.sh
mv ~/.ansible.cfg ~/.ansible.cfg-$(date -r ~/.ansible.cfg +%Y-%m-%d)
echo ". ~/.ansible.cfg.sh" >> ~/.bash_profile
. ~/.ansible.cfg.sh
'''),
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-c', help='format for csh/tcsh instead of sh/ksh/bash.',
action='store_true', default=False)
parser.add_argument('-a', help='include comments for all settings, not just those differing from their default values.',
action='store_true', default=False)
args = parser.parse_args()
# pprint.pprint(args)
# `ansible-config list` produces yaml output that describes all
# ansible configuration options.
cfg_list = subprocess.run(['ansible-config', 'list'], stdout=subprocess.PIPE)
cfg_ref = yaml.load(cfg_list.stdout.decode('utf-8'), Loader=yaml.SafeLoader)
# print("{}".format(cfg_ref))
cfg_dump = subprocess.run(['ansible-config', 'dump'], stdout=subprocess.PIPE)
# print("{}".format(cfg_dump.stdout.decode('utf-8')))
for line in cfg_dump.stdout.decode('utf-8').split("\n"):
ma = re.match("(\w+)\(([^()]+)\) *= *(.*)", line)
if ma:
_name = ma.group(1)
_from = ma.group(2)
_val = ma.group(3)
if args.a or _from != 'default':
print("### name: {}\n### from: {}\n### val: {}".format(_name, _from, _val))
if _name in cfg_ref:
print("# {}\n".format('\n# '.join(pprint.pformat(cfg_ref[_name]).splitlines())), end='')
print("{}\n".format(val2env(cfg_ref[_name], _val, _name, _from)))
else:
print("# No config definition for '{}'.".format(_name))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment