summaryrefslogtreecommitdiff
path: root/cloudinit/UserDataHandler.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/UserDataHandler.py')
-rw-r--r--cloudinit/UserDataHandler.py103
1 files changed, 60 insertions, 43 deletions
diff --git a/cloudinit/UserDataHandler.py b/cloudinit/UserDataHandler.py
index 541ee87a..93d1d36a 100644
--- a/cloudinit/UserDataHandler.py
+++ b/cloudinit/UserDataHandler.py
@@ -1,8 +1,10 @@
# vi: ts=4 expandtab
#
# Copyright (C) 2009-2010 Canonical Ltd.
+# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
#
# Author: Scott Moser <scott.moser@canonical.com>
+# Author: Juerg Hafliger <juerg.haefliger@hp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3, as
@@ -15,39 +17,41 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
import email
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
-from email import encoders
import yaml
import cloudinit
import cloudinit.util as util
import hashlib
-import os
import urllib
-starts_with_mappings={
- '#include' : 'text/x-include-url',
- '#include-once' : 'text/x-include-once-url',
- '#!' : 'text/x-shellscript',
- '#cloud-config' : 'text/cloud-config',
- '#upstart-job' : 'text/upstart-job',
- '#part-handler' : 'text/part-handler',
- '#cloud-boothook' : 'text/cloud-boothook',
- '#cloud-config-archive' : 'text/cloud-config-archive',
+
+starts_with_mappings = {
+ '#include': 'text/x-include-url',
+ '#include-once': 'text/x-include-once-url',
+ '#!': 'text/x-shellscript',
+ '#cloud-config': 'text/cloud-config',
+ '#upstart-job': 'text/upstart-job',
+ '#part-handler': 'text/part-handler',
+ '#cloud-boothook': 'text/cloud-boothook',
+ '#cloud-config-archive': 'text/cloud-config-archive',
}
-# if 'str' is compressed return decompressed otherwise return it
-def decomp_str(str):
+
+# if 'string' is compressed return decompressed otherwise return it
+def decomp_str(string):
import StringIO
import gzip
try:
- uncomp = gzip.GzipFile(None,"rb",1,StringIO.StringIO(str)).read()
+ uncomp = gzip.GzipFile(None, "rb", 1, StringIO.StringIO(string)).read()
return(uncomp)
except:
- return(str)
+ return(string)
+
def do_include(content, appendmsg):
import os
@@ -55,7 +59,8 @@ def do_include(content, appendmsg):
# also support '#include <url here>'
includeonce = False
for line in content.splitlines():
- if line == "#include": continue
+ if line == "#include":
+ continue
if line == "#include-once":
includeonce = True
continue
@@ -64,10 +69,11 @@ def do_include(content, appendmsg):
includeonce = True
elif line.startswith("#include"):
line = line[len("#include"):].lstrip()
- if line.startswith("#"): continue
+ if line.startswith("#"):
+ continue
# urls cannot not have leading or trailing white space
- msum = hashlib.md5()
+ msum = hashlib.md5() # pylint: disable=E1101
msum.update(line.strip())
includeonce_filename = "%s/urlcache/%s" % (
cloudinit.get_ipath_cur("data"), msum.hexdigest())
@@ -79,7 +85,7 @@ def do_include(content, appendmsg):
content = urllib.urlopen(line).read()
if includeonce:
util.write_file(includeonce_filename, content, mode=0600)
- except Exception as e:
+ except Exception:
raise
process_includes(message_from_string(decomp_str(content)), appendmsg)
@@ -88,14 +94,14 @@ def do_include(content, appendmsg):
def explode_cc_archive(archive, appendmsg):
for ent in yaml.load(archive):
# ent can be one of:
- # dict { 'filename' : 'value' , 'content' : 'value', 'type' : 'value' }
+ # dict { 'filename' : 'value', 'content' : 'value', 'type' : 'value' }
# filename and type not be present
# or
# scalar(payload)
-
+
def_type = "text/cloud-config"
- if isinstance(ent,str):
- ent = { 'content': ent }
+ if isinstance(ent, str):
+ ent = {'content': ent}
content = ent.get('content', '')
mtype = ent.get('type', None)
@@ -118,7 +124,7 @@ def explode_cc_archive(archive, appendmsg):
continue
msg.add_header(header, ent['header'])
- _attach_part(appendmsg,msg)
+ _attach_part(appendmsg, msg)
def multi_part_count(outermsg, newcount=None):
@@ -135,6 +141,7 @@ def multi_part_count(outermsg, newcount=None):
return(int(outermsg.get('Number-Attachments', 0)))
+
def _attach_part(outermsg, part):
"""
Attach an part to an outer message. outermsg must be a MIMEMultipart.
@@ -143,18 +150,20 @@ def _attach_part(outermsg, part):
cur = multi_part_count(outermsg)
if not part.get_filename(None):
part.add_header('Content-Disposition', 'attachment',
- filename = 'part-%03d' % (cur+1))
+ filename='part-%03d' % (cur + 1))
outermsg.attach(part)
- multi_part_count(outermsg, cur+1)
-
+ multi_part_count(outermsg, cur + 1)
+
+
def type_from_startswith(payload, default=None):
# slist is sorted longest first
- slist = sorted(starts_with_mappings.keys(), key=lambda e: 0-len(e))
+ slist = sorted(starts_with_mappings.keys(), key=lambda e: 0 - len(e))
for sstr in slist:
if payload.startswith(sstr):
return(starts_with_mappings[sstr])
return default
+
def process_includes(msg, appendmsg=None):
if appendmsg == None:
appendmsg = MIMEMultipart()
@@ -190,32 +199,36 @@ def process_includes(msg, appendmsg=None):
_attach_part(appendmsg, part)
-def message_from_string(data, headers={}):
+
+def message_from_string(data, headers=None):
+ if headers is None:
+ headers = {}
if "mime-version:" in data[0:4096].lower():
- was_mime = True
msg = email.message_from_string(data)
- for (key,val) in headers.items():
+ for (key, val) in headers.items():
if key in msg:
- msg.replace_header(key,val)
+ msg.replace_header(key, val)
else:
msg[key] = val
else:
- was_mime = False
- mtype = headers.get("Content-Type","text/plain")
+ mtype = headers.get("Content-Type", "text/plain")
maintype, subtype = mtype.split("/", 1)
msg = MIMEBase(maintype, subtype, *headers)
msg.set_payload(data)
return(msg)
+
# this is heavily wasteful, reads through userdata string input
def preprocess_userdata(data):
newmsg = MIMEMultipart()
process_includes(message_from_string(decomp_str(data)), newmsg)
return(newmsg.as_string())
-# callback is a function that will be called with (data, content_type, filename, payload)
-def walk_userdata(istr, callback, data = None):
+
+# callback is a function that will be called with (data, content_type,
+# filename, payload)
+def walk_userdata(istr, callback, data=None):
partnum = 0
for part in message_from_string(istr).walk():
# multipart/* are just containers
@@ -232,12 +245,16 @@ def walk_userdata(istr, callback, data = None):
callback(data, ctype, filename, part.get_payload(decode=True))
- partnum = partnum+1
+ partnum = partnum + 1
+
if __name__ == "__main__":
- import sys
- data = decomp_str(file(sys.argv[1]).read())
- newmsg = MIMEMultipart()
- process_includes(message_from_string(data), newmsg)
- print newmsg
- print "#found %s parts" % multi_part_count(newmsg)
+ def main():
+ import sys
+ data = decomp_str(file(sys.argv[1]).read())
+ newmsg = MIMEMultipart()
+ process_includes(message_from_string(data), newmsg)
+ print newmsg
+ print "#found %s parts" % multi_part_count(newmsg)
+
+ main()