diff --git a/cloudbaseinit/plugins/windows/userdata.py b/cloudbaseinit/plugins/windows/userdata.py index d1c749df..9088f1fd 100644 --- a/cloudbaseinit/plugins/windows/userdata.py +++ b/cloudbaseinit/plugins/windows/userdata.py @@ -59,49 +59,75 @@ class UserDataPlugin(base.BasePlugin): if reboot: break + if not reboot: + for handler_func in list(set(user_handlers.values())): + self._end_part_process_event(handler_func) + return (plugin_status, reboot) else: return self._process_non_multi_part(user_data) def _process_part(self, part, user_data_plugins, user_handlers): ret_val = None - content_type = part.get_content_type() - user_data_plugin = user_data_plugins.get(content_type) - if not user_data_plugin: - LOG.info("Userdata plugin not found for content type: %s" % - content_type) - else: - try: - if content_type == self._part_handler_content_type: - user_handlers.update(user_data_plugin.process(part)) + try: + content_type = part.get_content_type() + + handler_func = user_handlers.get(content_type) + if handler_func: + LOG.debug("Calling user part handler for content type: %s" % + content_type) + handler_func(None, content_type, part.get_filename(), + part.get_payload()) + else: + user_data_plugin = user_data_plugins.get(content_type) + if not user_data_plugin: + LOG.info("Userdata plugin not found for content type: %s" % + content_type) else: - handler_func = user_handlers.get(part.get_content_type()) - self._begin_part_process_event(part, handler_func) + LOG.debug("Executing userdata plugin: %s" % + user_data_plugin.__class__.__name__) - LOG.info("Executing user data plugin: %s" % - user_data_plugin.__class__.__name__) - - ret_val = user_data_plugin.process(part) - - self._end_part_process_event(part, handler_func) - except Exception, ex: - LOG.error('Exception during multipart part handling: ' - '%(content_type)s, %(filename)s' % - {'content_type': part.get_content_type(), - 'filename': part.get_filename()}) - LOG.exception(ex) + if content_type == self._part_handler_content_type: + new_user_handlers = user_data_plugin.process(part) + self._add_part_handlers(user_data_plugins, + user_handlers, + new_user_handlers) + else: + ret_val = user_data_plugin.process(part) + except Exception, ex: + LOG.error('Exception during multipart part handling: ' + '%(content_type)s, %(filename)s' % + {'content_type': part.get_content_type(), + 'filename': part.get_filename()}) + LOG.exception(ex) return self._get_plugin_return_value(ret_val) - def _begin_part_process_event(self, part, handler_func): - if handler_func: - handler_func("", "__begin__", part.get_filename(), - part.get_payload()) + def _add_part_handlers(self, user_data_plugins, user_handlers, + new_user_handlers): + handler_funcs = set() - def _end_part_process_event(self, part, handler_func): - if handler_func: - handler_func("", "__end__", part.get_filename(), - part.get_payload()) + for (content_type, + handler_func) in new_user_handlers.items(): + if not user_data_plugins.get(content_type): + LOG.info("Adding part handler for content " + "type: %s" % content_type) + user_handlers[content_type] = handler_func + handler_funcs.add(handler_func) + else: + LOG.info("Skipping part handler for content type \"%s\" as it " + "is already managed by a plugin" % content_type) + + for handler_func in handler_funcs: + self._begin_part_process_event(handler_func) + + def _begin_part_process_event(self, handler_func): + LOG.debug("Calling part handler \"__begin__\" event") + handler_func(None, "__begin__", None, None) + + def _end_part_process_event(self, handler_func): + LOG.debug("Calling part handler \"__end__\" event") + handler_func(None, "__end__", None, None) def _get_plugin_return_value(self, ret_val): plugin_status = base.PLUGIN_EXECUTION_DONE diff --git a/cloudbaseinit/plugins/windows/userdataplugins/cloudboothook.py b/cloudbaseinit/plugins/windows/userdataplugins/cloudboothook.py new file mode 100644 index 00000000..ddedd061 --- /dev/null +++ b/cloudbaseinit/plugins/windows/userdataplugins/cloudboothook.py @@ -0,0 +1,27 @@ +# Copyright 2014 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from cloudbaseinit.openstack.common import log as logging +from cloudbaseinit.plugins.windows.userdataplugins import base + +LOG = logging.getLogger(__name__) + + +class CloudBootHookPlugin(base.BaseUserDataPlugin): + def __init__(self): + super(CloudBootHookPlugin, self).__init__("text/cloud-boothook") + + def process(self, part): + LOG.info("%s content is currently not supported" % + self.get_mime_type()) diff --git a/cloudbaseinit/plugins/windows/userdataplugins/cloudconfig.py b/cloudbaseinit/plugins/windows/userdataplugins/cloudconfig.py index ba20f73d..a593b902 100644 --- a/cloudbaseinit/plugins/windows/userdataplugins/cloudconfig.py +++ b/cloudbaseinit/plugins/windows/userdataplugins/cloudconfig.py @@ -24,4 +24,5 @@ class CloudConfigPlugin(base.BaseUserDataPlugin): super(CloudConfigPlugin, self).__init__("text/cloud-config") def process(self, part): - LOG.info("text/cloud-config content is not currently supported") + LOG.info("%s content is currently not supported" % + self.get_mime_type()) diff --git a/cloudbaseinit/plugins/windows/userdataplugins/factory.py b/cloudbaseinit/plugins/windows/userdataplugins/factory.py index be96bb41..4c58d154 100644 --- a/cloudbaseinit/plugins/windows/userdataplugins/factory.py +++ b/cloudbaseinit/plugins/windows/userdataplugins/factory.py @@ -21,12 +21,16 @@ opts = [ default=[ 'cloudbaseinit.plugins.windows.userdataplugins.parthandler.' 'PartHandlerPlugin', - 'cloudbaseinit.plugins.windows.userdataplugins.heat.' - 'HeatPlugin', 'cloudbaseinit.plugins.windows.userdataplugins.cloudconfig.' 'CloudConfigPlugin', + 'cloudbaseinit.plugins.windows.userdataplugins.cloudboothook.' + 'CloudBootHookPlugin', 'cloudbaseinit.plugins.windows.userdataplugins.shellscript.' 'ShellScriptPlugin', + 'cloudbaseinit.plugins.windows.userdataplugins.multipartmixed.' + 'MultipartMixedPlugin', + 'cloudbaseinit.plugins.windows.userdataplugins.heat.' + 'HeatPlugin', ], help='List of enabled userdata content plugins'), ] diff --git a/cloudbaseinit/plugins/windows/userdataplugins/heat.py b/cloudbaseinit/plugins/windows/userdataplugins/heat.py index e5d9abbf..a20720b9 100644 --- a/cloudbaseinit/plugins/windows/userdataplugins/heat.py +++ b/cloudbaseinit/plugins/windows/userdataplugins/heat.py @@ -27,7 +27,7 @@ class HeatPlugin(base.BaseUserDataPlugin): super(HeatPlugin, self).__init__("text/x-cfninitdata") def process(self, part): - # Only user-data part of Heat multipart data is supported. - # All other cfinitdata part will be skipped if part.get_filename() == self._heat_user_data_filename: return userdatautils.execute_user_data_script(part.get_payload()) + else: + LOG.info("Heat content not supported: %s" % part.get_filename()) diff --git a/cloudbaseinit/plugins/windows/userdataplugins/multipartmixed.py b/cloudbaseinit/plugins/windows/userdataplugins/multipartmixed.py new file mode 100644 index 00000000..b2f07279 --- /dev/null +++ b/cloudbaseinit/plugins/windows/userdataplugins/multipartmixed.py @@ -0,0 +1,23 @@ +# Copyright 2014 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from cloudbaseinit.plugins.windows.userdataplugins import base + + +class MultipartMixedPlugin(base.BaseUserDataPlugin): + def __init__(self): + super(MultipartMixedPlugin, self).__init__("multipart/mixed") + + def process(self, part): + pass