summaryrefslogtreecommitdiff
path: root/cloudinit/util.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/util.py')
-rw-r--r--cloudinit/util.py174
1 files changed, 170 insertions, 4 deletions
diff --git a/cloudinit/util.py b/cloudinit/util.py
index c1b4fd2d..e958fc02 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -17,13 +17,16 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import yaml
import os
+import os.path
import errno
import subprocess
from Cheetah.Template import Template
import cloudinit
import urllib2
+import urllib
import logging
import traceback
+import re
WARN = logging.WARN
DEBUG = logging.DEBUG
@@ -40,13 +43,32 @@ def read_conf(fname):
return { }
raise
-def get_base_cfg(cfgfile,cfg_builtin=""):
- syscfg = read_conf(cfgfile)
+def get_base_cfg(cfgfile,cfg_builtin="", parsed_cfgs=None):
+ kerncfg = { }
+ syscfg = { }
+ if parsed_cfgs and cfgfile in parsed_cfgs:
+ return(parsed_cfgs[cfgfile])
+
+ contents = read_file_with_includes(cfgfile)
+ if contents:
+ syscfg = yaml.load(contents)
+
+ kern_contents = read_cc_from_cmdline()
+ if kern_contents:
+ kerncfg = yaml.load(kern_contents)
+
+ # kernel parameters override system config
+ combined = mergedict(kerncfg, syscfg)
+
if cfg_builtin:
builtin = yaml.load(cfg_builtin)
+ fin = mergedict(combined,builtin)
else:
- return(syscfg)
- return(mergedict(syscfg,builtin))
+ fin = combined
+
+ if parsed_cfgs != None:
+ parsed_cfgs[cfgfile] = fin
+ return(fin)
def get_cfg_option_bool(yobj, key, default=False):
if not yobj.has_key(key): return default
@@ -106,6 +128,16 @@ def getkeybyid(keyid,keyserver):
args=['sh', '-c', shcmd, "export-gpg-keyid", keyid, keyserver]
return(subp(args)[0])
+def runparts(dirp, skip_no_exist=True):
+ if skip_no_exist and not os.path.isdir(dirp): return
+
+ cmd = [ 'run-parts', '--regex', '.*', dirp ]
+ sp = subprocess.Popen(cmd)
+ sp.communicate()
+ if sp.returncode is not 0:
+ raise subprocess.CalledProcessError(sp.returncode,cmd)
+ return
+
def subp(args, input=None):
s_in = None
if input is not None:
@@ -122,6 +154,10 @@ def render_to_file(template, outfile, searchList):
f.write(t.respond())
f.close()
+def render_string(template, searchList):
+ return(Template(template, searchList=[searchList]).respond())
+
+
# read_optional_seed
# returns boolean indicating success or failure (presense of files)
# if files are present, populates 'fill' dictionary with 'user-data' and
@@ -168,3 +204,133 @@ def read_seeded(base="", ext="", timeout=2):
def logexc(log,lvl=logging.DEBUG):
log.log(lvl,traceback.format_exc())
+
+class RecursiveInclude(Exception):
+ pass
+
+def read_file_with_includes(fname, rel = ".", stack=[], patt = None):
+ if not fname.startswith("/"):
+ fname = os.sep.join((rel, fname))
+
+ fname = os.path.realpath(fname)
+
+ if fname in stack:
+ raise(RecursiveInclude("%s recursively included" % fname))
+ if len(stack) > 10:
+ raise(RecursiveInclude("%s included, stack size = %i" %
+ (fname, len(stack))))
+
+ if patt == None:
+ patt = re.compile("^#(opt_include|include)[ \t].*$",re.MULTILINE)
+
+ try:
+ fp = open(fname)
+ contents = fp.read()
+ fp.close()
+ except:
+ raise
+
+ rel = os.path.dirname(fname)
+ stack.append(fname)
+
+ cur = 0
+ clen = len(contents)
+ while True:
+ match = patt.search(contents[cur:])
+ if not match: break
+ loc = match.start() + cur
+ endl = match.end() + cur
+
+ (key, cur_fname) = contents[loc:endl].split(None,2)
+ cur_fname = cur_fname.strip()
+
+ try:
+ inc_contents = read_file_with_includes(cur_fname, rel, stack, patt)
+ except IOError, e:
+ if e.errno == errno.ENOENT and key == "#opt_include":
+ inc_contents = ""
+ else:
+ raise
+ contents = contents[0:loc] + inc_contents + contents[endl+1:]
+ cur = loc + len(inc_contents)
+ stack.pop()
+ return(contents)
+
+def get_cmdline():
+ if 'DEBUG_PROC_CMDLINE' in os.environ:
+ cmdline = os.environ["DEBUG_PROC_CMDLINE"]
+ else:
+ try:
+ cmdfp = open("/proc/cmdline")
+ cmdline = cmdfp.read().strip()
+ cmdfp.close()
+ except:
+ cmdline = ""
+ return(cmdline)
+
+def read_cc_from_cmdline(cmdline=None):
+ # this should support reading cloud-config information from
+ # the kernel command line. It is intended to support content of the
+ # format:
+ # cc: <yaml content here> [end_cc]
+ # this would include:
+ # cc: ssh_import_id: [smoser, kirkland]\\n
+ # cc: ssh_import_id: [smoser, bob]\\nruncmd: [ [ ls, -l ], echo hi ] end_cc
+ # cc:ssh_import_id: [smoser] end_cc cc:runcmd: [ [ ls, -l ] ] end_cc
+ if cmdline is None:
+ cmdline = get_cmdline()
+
+ tag_begin="cc:"
+ tag_end="end_cc"
+ begin_l = len(tag_begin)
+ end_l = len(tag_end)
+ clen = len(cmdline)
+ tokens = [ ]
+ begin = cmdline.find(tag_begin)
+ while begin >= 0:
+ end = cmdline.find(tag_end, begin + begin_l)
+ if end < 0:
+ end = clen
+ tokens.append(cmdline[begin+begin_l:end].lstrip().replace("\\n","\n"))
+
+ begin = cmdline.find(tag_begin, end + end_l)
+
+ return('\n'.join(tokens))
+
+def ensure_dirs(dirlist, mode=0755):
+ fixmodes = []
+ for d in dirlist:
+ try:
+ if mode != None:
+ os.makedirs(d)
+ else:
+ os.makedirs(d, mode)
+ except OSError as e:
+ if e.errno != errno.EEXIST: raise
+ if mode != None: fixmodes.append(d)
+
+ for d in fixmodes:
+ os.chmod(d, mode)
+
+def chownbyname(fname,user=None,group=None):
+ uid = -1
+ gid = -1
+ if user == None and group == None: return
+ if user:
+ import pwd
+ uid = pwd.getpwnam(user).pw_uid
+ if group:
+ import grp
+ gid = grp.getgrnam(group).gr_gid
+
+ os.chown(fname,uid,gid)
+
+def readurl(url, data=None):
+ if data is None:
+ req = urllib2.Request(url)
+ else:
+ encoded = urllib.urlencode(data)
+ req = urllib2.Request(url, encoded)
+
+ response = urllib2.urlopen(req)
+ return(response.read())