summaryrefslogtreecommitdiff
path: root/cloudinit
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit')
-rw-r--r--cloudinit/cloud.py7
-rw-r--r--cloudinit/sources/DataSourceEc2.py3
-rw-r--r--cloudinit/sources/__init__.py49
-rw-r--r--cloudinit/stages.py2
-rw-r--r--cloudinit/user_data.py50
5 files changed, 90 insertions, 21 deletions
diff --git a/cloudinit/cloud.py b/cloudinit/cloud.py
index 620b3c07..af69a541 100644
--- a/cloudinit/cloud.py
+++ b/cloudinit/cloud.py
@@ -70,12 +70,15 @@ class Cloud(object):
return fn
# The rest of thes are just useful proxies
- def get_userdata(self):
- return self.datasource.get_userdata()
+ def get_userdata(self, apply_filter=True):
+ return self.datasource.get_userdata(apply_filter)
def get_instance_id(self):
return self.datasource.get_instance_id()
+ def get_launch_index(self):
+ return self.datasource.get_launch_index()
+
def get_public_ssh_keys(self):
return self.datasource.get_public_ssh_keys()
diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py
index 556dcafb..3e450e7e 100644
--- a/cloudinit/sources/DataSourceEc2.py
+++ b/cloudinit/sources/DataSourceEc2.py
@@ -77,6 +77,9 @@ class DataSourceEc2(sources.DataSource):
self.metadata_address)
return False
+ def get_launch_index(self):
+ return self.metadata.get('ami-launch-index')
+
def get_instance_id(self):
return self.metadata['instance-id']
diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py
index 4719d254..a1939d58 100644
--- a/cloudinit/sources/__init__.py
+++ b/cloudinit/sources/__init__.py
@@ -20,6 +20,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from email.mime.multipart import MIMEMultipart
+
import abc
from cloudinit import importer
@@ -59,12 +61,53 @@ class DataSource(object):
else:
self.ud_proc = ud_proc
- def get_userdata(self):
+ def get_userdata(self, apply_filter=False):
if self.userdata is None:
- raw_data = self.get_userdata_raw()
- self.userdata = self.ud_proc.process(raw_data)
+ self.userdata = self.ud_proc.process(self.get_userdata_raw())
+ if apply_filter:
+ return self._filter_userdata(self.userdata)
return self.userdata
+ def get_launch_index(self):
+ return None
+
+ def _filter_userdata(self, processed_ud):
+ idx = self.get_launch_index()
+ if idx is None:
+ return processed_ud
+ # First do a scan to see if any one with launch-index
+ # headers, if not just skip this....
+ launch_idxs = 0
+ for part in processed_ud.walk():
+ # multipart/* are just containers
+ if part.get_content_maintype() == 'multipart':
+ continue
+ launch_idx_h = part.get('Launch-Index', None)
+ if launch_idx_h is not None:
+ launch_idxs += 1
+ if not launch_idxs:
+ return processed_ud
+ # Reform a new message with those that either have
+ # no launch index or ones that have our launch index or ones
+ # that have some other garbage that we don't know what to do with
+ accumulating_msg = MIMEMultipart()
+ tot_attached = 0
+ for part in processed_ud.walk():
+ # multipart/* are just containers
+ if part.get_content_maintype() == 'multipart':
+ continue
+ try:
+ launch_idx_h = part.get('Launch-Index', None)
+ if launch_idx_h is None or int(launch_idx_h) == int(idx):
+ accumulating_msg.attach(part)
+ tot_attached += 1
+ except:
+ # If any int conversion fails (or other error), keep the part
+ accumulating_msg.attach(part)
+ tot_attached += 1
+ accumulating_msg[ud.ATTACHMENT_FIELD] = str(tot_attached)
+ return accumulating_msg
+
@property
def is_disconnected(self):
return False
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index c9634a90..af902925 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -347,7 +347,7 @@ class Init(object):
sys.path.insert(0, idir)
# Ensure datasource fetched before activation (just incase)
- user_data_msg = self.datasource.get_userdata()
+ user_data_msg = self.datasource.get_userdata(True)
# This keeps track of all the active handlers
c_handlers = helpers.ContentHandlers()
diff --git a/cloudinit/user_data.py b/cloudinit/user_data.py
index af98b488..244e9223 100644
--- a/cloudinit/user_data.py
+++ b/cloudinit/user_data.py
@@ -58,10 +58,9 @@ class UserDataProcessor(object):
self.paths = paths
def process(self, blob):
- base_msg = convert_string(blob)
- process_msg = MIMEMultipart()
- self._process_msg(base_msg, process_msg)
- return process_msg
+ accumulating_msg = MIMEMultipart()
+ self._process_msg(convert_string(blob), accumulating_msg)
+ return accumulating_msg
def _process_msg(self, base_msg, append_msg):
for part in base_msg.walk():
@@ -97,11 +96,38 @@ class UserDataProcessor(object):
self._attach_part(append_msg, part)
+ def _attach_launch_index(self, msg):
+ header_idx = msg.get('Launch-Index', None)
+ payload_idx = None
+ try:
+ payload = util.load_yaml(msg.get_payload(decode=True))
+ if payload:
+ payload_idx = payload.get('launch-index')
+ except:
+ pass
+ # Header overrides contents...
+ if header_idx is not None:
+ payload_idx = header_idx
+ # Nothing found in payload, use header (if anything there)
+ if payload_idx is None:
+ payload_idx = header_idx
+ if payload_idx is not None:
+ try:
+ msg.add_header('Launch-Index', str(int(payload_idx)))
+ except:
+ pass
+
def _get_include_once_filename(self, entry):
entry_fn = util.hash_blob(entry, 'md5', 64)
return os.path.join(self.paths.get_ipath_cur('data'),
'urlcache', entry_fn)
+ def _process_before_attach(self, msg, attached_id):
+ if not msg.get_filename():
+ msg.add_header('Content-Disposition',
+ 'attachment', filename=PART_FN_TPL % (attached_id))
+ self._attach_launch_index(msg)
+
def _do_include(self, content, append_msg):
# Include a list of urls, one per line
# also support '#include <url here>'
@@ -204,21 +230,15 @@ class UserDataProcessor(object):
outer_msg.replace_header(ATTACHMENT_FIELD, str(fetched_count))
return fetched_count
- def _part_filename(self, _unnamed_part, count):
- return PART_FN_TPL % (count + 1)
-
def _attach_part(self, outer_msg, part):
"""
- Attach an part to an outer message. outermsg must be a MIMEMultipart.
- Modifies a header in the message to keep track of number of attachments.
+ Attach a message to an outer message. outermsg must be a MIMEMultipart.
+ Modifies a header in the outer message to keep track of number of attachments.
"""
- cur_c = self._multi_part_count(outer_msg)
- if not part.get_filename():
- fn = self._part_filename(part, cur_c)
- part.add_header('Content-Disposition',
- 'attachment', filename=fn)
+ part_count = self._multi_part_count(outer_msg)
+ self._process_before_attach(part, part_count + 1)
outer_msg.attach(part)
- self._multi_part_count(outer_msg, cur_c + 1)
+ self._multi_part_count(outer_msg, part_count + 1)
# Coverts a raw string into a mime message