Skip to content

Instantly share code, notes, and snippets.

@smoser
Last active December 22, 2015 11:38
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 smoser/6466708 to your computer and use it in GitHub Desktop.
Save smoser/6466708 to your computer and use it in GitHub Desktop.
parse_shell_config: basically reads a "shell config" in python by running it with bash, and then reading the variables declared. It returns an dict with the variables found after running that code. the point of using this is that some software has "config" in shell format (ie, 'openrc' in openstack).
DISK_ID='1'
ETH0_DNS='10.0.0.1'
ETH0_GATEWAY='10.0.0.1'
ETH0_IP='10.0.0.72'
ETH0_MASK='255.255.255.0'
FILES_DS='/var/lib/one/datastores/2/9c92ad910c0b30a411ccdfc141bc7a25:'\''jfopet.sh'\'' '
FILES_URL='http://10.0.0.11/files/installer.tar.gz'
INIT_SCRIPTS='jfopet.sh'
MANIFEST='ubuntu-kvm1'
SSH_PUBLIC_KEY='ssh-dss AAAAB3NzaC1kc3MAAACBANprdUVFRaADZXZaAm2elpRaUGCMETLLuYYJCDUZPb0Dh/V7KeM2/a3rFIDA0s5sVK2XQNqLHyy0U8xA/0R8dplmg7BDckkAzhrDVpEGnQE1fk/xPd1t7u+yeVqpbrfyAmfmyE2P980mhBoWalbeV/f7SmHUP8RiQ2hlAWUxr7I5AAAAFQDLHhFndbcA7svGd/yfY6nU4ubodwAAAIAoXLKlckmZur1pc9TN4XoHa+Fl/6Qpu0XO7Ai/tu8dqHlN+FpVk8BQNnokwE+EZBARLIL0JCjHpT9b5aEvlpRz3TuDa6az8wvJRlScNufmVrf0ls1WCFDHujSLzd3aOkKct35Aamf1amP/NPE2aGne/tPS7HQ3TCf5E2MAQzYVJKSARwAAAIAaVqvU7LfGMEw0hEXr7fuJCMHh4FmPvejiOoSUz2GOU5bceasRitdulCQJbNiVtY6U0S+qQ0X8rvAnG934p4zS9TtgKIhplr146fkbYnNjCaAM0rNVvTh2SzEEKJiG2G1d3wyNuO8wpPhIiJ1OVZrGwkVyWwiNzC2sWXAXldQ9Hg== user1@host
ssh-dss AAAAB3NzaC1kc3MAAACBANBWTQmm4GtkAiPpA20DK1TQd1n7UoC5BymTHlGzFmj+BsQdn6ZZbihV/roNVo1roJhb2hQq/WuQR50D72vMIjrC6t1DkofFBL0iz+mb7JdmNxbE7cnOHMxEr/cd4ds4EwzpBKiQzt8NNcz/zbSadHQtBd0+u1G5nvm4MXHNVYQnAAAAFQDbEN554Or4vd7D+2wzjs3c1nNOowAAAIAdRlTrRG1YmceKHh3urcltniIoo8FrNudwCShbHTCOQbM+KkMUTtw5qwWFuJP6HdaLjmUehqxqDWeWGp8c2y5yMee0JR5cx+iMwhg1Q2o4S6c+zgWdUYIVkuPgYOOR2GMCPdl9mwcxtVvpHD59UFlPh16oLzakUCSxkro5V/LUXxuywAAAIBtYvxfwI5Cl0xq3/KQI7giNef05EXIgK+KwYu3xC6fs+7NxQYFzMsSEQhEJI62J091Kh0RFpFPdGPECIQolt8j4ltymKM9+pfgiE7oBXSxkW44XadBnCWCGU5B4gTnz84VKECWuu2J9Z4cn44hKYt1uj0SxxzExnB1X51kUN+Z5Q== user2@host'
#!/bin/bash
NOVARC_CONFIG_VERSION=1
# allow python-keyring usage, as configured in
# HOME/.local/share/python_keyring/keyringrc.cfg
export OS_NO_CACHE=${OS_NO_CACHE:-0}
NOVARC=$(readlink -f "${BASH_SOURCE:-${0}}" 2>/dev/null) ||
NOVARC=$(python -c 'import os,sys; print os.path.abspath(os.path.realpath(sys.argv[1]))' "${BASH_SOURCE:-${0}}")
NOVA_KEY_DIR=${NOVARC%/*}
export OS_USERNAME="smoser"
export OS_TENANT_NAME="smoser_project"
export OS_PASSWORD="ABCDEFGHIJKL"
export OS_AUTH_URL="https://keystone.openstack.example.com:443/v2.0/"
export OS_REGION_NAME="${OS_REGION_NAME:-dtw01}"
export NOVA_USERNAME="${OS_USERNAME}"
export NOVA_PASSWORD="${OS_PASSWORD}"
export NOVA_PROJECT_ID="${OS_TENANT_NAME}"
export NOVA_VERSION="1.1"
export NOVA_REGION="${OS_REGION_NAME}"
export EC2_ACCESS_KEY="abcdef0123456789abcdef0123456789"
export EC2_SECRET_KEY="0123456789abcdef0123456789abcdef"
export EC2_URL="https://ec2-${OS_REGION_NAME}.openstack.example.com:443/services/Cloud"
export S3_URL="http://s3-${OS_REGION_NAME}.openstack.example.com:3333"
export EC2_USER_ID=42 # nova does not use user id, but bundling requires it
export EC2_PRIVATE_KEY=${NOVA_KEY_DIR}/pk.pem
export EC2_CERT=${NOVA_KEY_DIR}/cert.pem
export NOVA_CERT=${NOVA_KEY_DIR}/cacert.pem
export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this set
alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user 42 --ec2cert ${NOVA_CERT}"
alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${NOVA_CERT}"
export NOVA_API_KEY="${EC2_ACCESS_KEY}"
#!/usr/bin/python
import subprocess
import string
def parse_shell_config(content=None, filepath=None, keylist=None, bash=None,
asuser=None):
if content is None and filepath is None:
raise ValueError("content and filepath cannot be None")
elif content is not None and filepath is not None:
raise ValueError("content and filepath cannot both be set")
if isinstance(bash, str):
bash = [bash]
elif bash is None:
bash = ['bash', '-e']
if filepath is not None:
content = ". '%s'" % filepath
# allvars expands to all existing variables by using '${!x*}' notation
# where x is lower or upper case letters or '_'
allvars = ["${!%s*}" % x for x in string.letters + "_"]
keylist_in = keylist
if keylist is None:
keylist = allvars
keylist_in = []
setup = '\n'.join(('__v="";', '',))
def varprinter(vlist):
# output '\0'.join(['_start_', key=value NULL for vars in vlist]
return '\n'.join((
'printf "%s\\0" _start_',
'for __v in %s; do' % ' '.join(vlist),
' printf "%s=%s\\0" "$__v" "${!__v}";',
'done',
''
))
# the rendered 'bcmd' is bash syntax that does
# setup: declare variables we use (so they show up in 'all')
# varprinter(allvars): print all variables known at beginning
# content: execute the provided content
# varprinter(keylist): print all variables known after content
#
# output is then a null terminated array of:
# literal '_start_'
# key=value (for each preset variable)
# literal '_start_'
# key=value (for each post set variable)
bcmd = ('unset IFS\n' +
setup +
varprinter(allvars) +
'{\n%s\n\n} > /dev/null\n' % content +
'unset IFS\n' +
varprinter(keylist) + "\n")
cmd = []
if asuser is not None:
cmd = ['sudo', '-u', asuser]
cmd.extend(bash)
sp = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
(output, error) = sp.communicate(input=bcmd)
if sp.returncode != 0:
raise Exception("Process returned %d" % sp.returncode)
# exclude vars in bash that change on their own or that we used
excluded = ("RANDOM", "LINENO", "_", "__v")
preset = {}
ret = {}
target = None
output = output[0:-1] # remove trailing null
# go through output. First _start_ is for 'preset', second for 'target'.
# Add to target only things were changed and not in volitile
for line in output.split("\0"):
try:
(key, val) = line.split("=", 1)
if target is preset:
target[key] = val
elif (key not in excluded and
(key in keylist_in or preset.get(key) != val)):
ret[key] = val
except ValueError:
if line != "_start_":
raise
if target is None:
target = preset
elif target is preset:
target = ret
return ret
if __name__ == '__main__':
import pprint
import sys
with open(sys.argv[1], "r") as fp:
content = fp.read()
asuser = None
if len(sys.argv) > 2:
asuser = sys.argv[2]
#pprint.pprint(parse_shell_config(filepath=sys.argv[1], asuser=asuser))
pprint.pprint(parse_shell_config(content=content, asuser=asuser))
@harlowja
Copy link

harlowja commented Sep 7, 2013

U da bomb!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment