summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Harlow <harlowja@yahoo-inc.com>2012-06-16 10:56:41 -0700
committerJoshua Harlow <harlowja@yahoo-inc.com>2012-06-16 10:56:41 -0700
commitdb19f526b65f11ecabfbdbe86cb546881727d647 (patch)
treea3d2cb0d939943cf2fc3a657d5396f734cf62e3a
parent5597e947248ee7fbda17d3876206f1d45f97bc00 (diff)
downloadvyos-cloud-init-db19f526b65f11ecabfbdbe86cb546881727d647.tar.gz
vyos-cloud-init-db19f526b65f11ecabfbdbe86cb546881727d647.zip
Move the helper functions for handlers to the handler __init__ instead of being in here.
This class will now just contain user data parsing, leaving the handler running to happen elsewhere.
-rw-r--r--cloudinit/user_data.py196
1 files changed, 10 insertions, 186 deletions
diff --git a/cloudinit/user_data.py b/cloudinit/user_data.py
index 28c9f2ce..2f01579a 100644
--- a/cloudinit/user_data.py
+++ b/cloudinit/user_data.py
@@ -20,7 +20,6 @@
# 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 abc
import os
import email
@@ -28,56 +27,24 @@ from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
-from cloudinit import importer
+from cloudinit import handlers
from cloudinit import log as logging
from cloudinit import url_helper
from cloudinit import util
-from cloudinit.settings import (PER_ALWAYS, PER_INSTANCE, FREQUENCIES)
-
LOG = logging.getLogger(__name__)
-# Special content types that signal the start and end of processing
-CONTENT_END = "__end__"
-CONTENT_START = "__begin__"
-CONTENT_SIGNALS = [CONTENT_START, CONTENT_END]
-
-# Used when a part-handler type is encountered
-# to allow for registration of new types.
-PART_CONTENT_TYPES = ["text/part-handler"]
-PART_HANDLER_FN_TMPL = 'part-handler-%03d'
-
-# For parts without filenames
-PART_FN_TPL = 'part-%03d'
-
-# Used as the content type when a message is not multipart
-# and it doesn't contain its own content-type
-NOT_MULTIPART_TYPE = "text/x-not-multipart"
-OCTET_TYPE = 'application/octet-stream'
-
-# Different file beginnings to there content type
-INCLUSION_TYPES_MAP = {
- '#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',
-}
+# Constants copied in from the handler module
+NOT_MULTIPART_TYPE = handlers.NOT_MULTIPART_TYPE
+PART_FN_TPL = handlers.PART_FN_TPL
+OCTET_TYPE = handlers.OCTET_TYPE
-# Sorted longest first
-INCLUSION_SRCH = sorted(list(INCLUSION_TYPES_MAP.keys()),
- key=(lambda e: 0 - len(e)))
-
-# Various special content types
+# Various special content types that cause special actions
TYPE_NEEDED = ["text/plain", "text/x-not-multipart"]
INCLUDE_TYPES = ['text/x-include-url', 'text/x-include-once-url']
ARCHIVE_TYPES = ["text/cloud-config-archive"]
UNDEF_TYPE = "text/plain"
ARCHIVE_UNDEF_TYPE = "text/cloud-config"
-OCTET_TYPE = 'application/octet-stream'
# Msg header used to track attachments
ATTACHMENT_FIELD = 'Number-Attachments'
@@ -107,7 +74,7 @@ class UserDataProcessor(object):
ctype_orig = UNDEF_TYPE
if ctype_orig in TYPE_NEEDED:
- ctype = type_from_starts_with(payload)
+ ctype = handlers.type_from_starts_with(payload)
if ctype is None:
ctype = ctype_orig
@@ -181,7 +148,8 @@ class UserDataProcessor(object):
content = ent.get('content', '')
mtype = ent.get('type')
if not mtype:
- mtype = type_from_starts_with(content, ARCHIVE_UNDEF_TYPE)
+ mtype = handlers.type_from_starts_with(content,
+ ARCHIVE_UNDEF_TYPE)
maintype, subtype = mtype.split('/', 1)
if maintype == "text":
@@ -232,146 +200,6 @@ class UserDataProcessor(object):
self._multi_part_count(outer_msg, cur + 1)
-class PartHandler(object):
-
- __metaclass__ = abc.ABCMeta
-
- def __init__(self, frequency, version=2):
- self.handler_version = version
- self.frequency = frequency
-
- def __repr__(self):
- return "%s: [%s]" % (util.obj_name(self), self.list_types())
-
- @abc.abstractmethod
- def list_types(self):
- raise NotImplementedError()
-
- def handle_part(self, data, ctype, filename, payload, frequency):
- return self._handle_part(data, ctype, filename, payload, frequency)
-
- @abc.abstractmethod
- def _handle_part(self, data, ctype, filename, payload, frequency):
- raise NotImplementedError()
-
-
-def fixup_handler(mod, def_freq=PER_INSTANCE):
- if not hasattr(mod, "handler_version"):
- setattr(mod, "handler_version", 1)
- if not hasattr(mod, 'list_types'):
- def empty_types():
- return []
- setattr(mod, 'list_types', empty_types)
- if not hasattr(mod, 'frequency'):
- setattr(mod, 'frequency', def_freq)
- else:
- freq = mod.frequency
- if freq and freq not in FREQUENCIES:
- LOG.warn("Module %s has an unknown frequency %s", mod, freq)
- if not hasattr(mod, 'handle_part'):
- def empty_handler(_data, _ctype, _filename, _payload):
- pass
- setattr(mod, 'handle_part', empty_handler)
- return mod
-
-
-def run_part(mod, data, ctype, filename, payload, frequency):
- mod_freq = mod.frequency
- if not (mod_freq == PER_ALWAYS or
- (frequency == PER_INSTANCE and mod_freq == PER_INSTANCE)):
- return
- mod_ver = mod.handler_version
- try:
- if mod_ver == 1:
- mod.handle_part(data, ctype, filename, payload)
- else:
- mod.handle_part(data, ctype, filename, payload, frequency)
- except:
- util.logexc(LOG, ("Failed calling mod %s (%s, %s, %s)"
- " with frequency %s"),
- mod, ctype, filename,
- mod_ver, frequency)
-
-
-def call_begin(mod, data, frequency):
- run_part(mod, data, CONTENT_START, None, None, frequency)
-
-
-def call_end(mod, data, frequency):
- run_part(mod, data, CONTENT_END, None, None, frequency)
-
-
-def walker_handle_handler(pdata, _ctype, _filename, payload):
- curcount = pdata['handlercount']
- modname = PART_HANDLER_FN_TMPL % (curcount)
- frequency = pdata['frequency']
- modfname = os.path.join(pdata['handlerdir'], "%s" % (modname))
- if not modfname.endswith(".py"):
- modfname = "%s.py" % (modfname)
- # TODO: Check if path exists??
- util.write_file(modfname, payload, 0600)
- handlers = pdata['handlers']
- try:
- mod = fixup_handler(importer.import_module(modname))
- handlers.register(mod)
- call_begin(mod, pdata['data'], frequency)
- pdata['handlercount'] = curcount + 1
- except:
- util.logexc(LOG, "Failed at registered python file: %s", modfname)
-
-
-def extract_first_or_bytes(blob, size):
- # Extract the first line upto X bytes or X bytes from more than the
- # first line if the first line does not contain enough bytes
- first_line = blob.split("\n", 1)[0]
- if len(first_line) >= size:
- start = first_line[:size]
- else:
- start = blob[0:size]
- return start
-
-
-def walker_callback(pdata, ctype, filename, payload):
- if ctype in PART_CONTENT_TYPES:
- walker_handle_handler(pdata, ctype, filename, payload)
- return
- handlers = pdata['handlers']
- if ctype not in handlers:
- # Extract the first line or 24 bytes for displaying in the log
- start = extract_first_or_bytes(payload, 24)
- details = "'%s...'" % (start.encode("string-escape"))
- if ctype == NOT_MULTIPART_TYPE:
- LOG.warning("Unhandled non-multipart (%s) userdata: %s",
- ctype, details)
- else:
- LOG.warning("Unhandled unknown content-type (%s) userdata: %s",
- ctype, details)
- else:
- run_part(handlers[ctype], pdata['data'], ctype, filename,
- payload, pdata['frequency'])
-
-
-# Callback is a function that will be called with
-# (data, content_type, filename, payload)
-def walk(msg, callback, data):
- partnum = 0
- for part in msg.walk():
- # multipart/* are just containers
- if part.get_content_maintype() == 'multipart':
- continue
-
- ctype = part.get_content_type()
- if ctype is None:
- ctype = OCTET_TYPE
-
- filename = part.get_filename()
- if not filename:
- filename = PART_FN_TPL % (partnum)
-
- callback(data, ctype, filename, part.get_payload(decode=True))
- partnum = partnum + 1
-
-
# Coverts a raw string into a mime message
def convert_string(raw_data, headers=None):
if not raw_data:
@@ -394,9 +222,5 @@ def convert_string(raw_data, headers=None):
return msg
-def type_from_starts_with(payload, default=None):
- for text in INCLUSION_SRCH:
- if payload.startswith(text):
- return INCLUSION_TYPES_MAP[text]
- return default
+