OLD | NEW |
1 # Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/ | 1 # Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/ |
2 # Copyright (c) 2010, Eucalyptus Systems, Inc. | 2 # Copyright (c) 2010, Eucalyptus Systems, Inc. |
3 # All rights reserved. | 3 # All rights reserved. |
4 # | 4 # |
5 # Permission is hereby granted, free of charge, to any person obtaining a | 5 # Permission is hereby granted, free of charge, to any person obtaining a |
6 # copy of this software and associated documentation files (the | 6 # copy of this software and associated documentation files (the |
7 # "Software"), to deal in the Software without restriction, including | 7 # "Software"), to deal in the Software without restriction, including |
8 # without limitation the rights to use, copy, modify, merge, publish, dis- | 8 # without limitation the rights to use, copy, modify, merge, publish, dis- |
9 # tribute, sublicense, and/or sell copies of the Software, and to permit | 9 # tribute, sublicense, and/or sell copies of the Software, and to permit |
10 # persons to whom the Software is furnished to do so, subject to the fol- | 10 # persons to whom the Software is furnished to do so, subject to the fol- |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
47 import logging.handlers | 47 import logging.handlers |
48 import boto | 48 import boto |
49 import tempfile | 49 import tempfile |
50 import smtplib | 50 import smtplib |
51 import datetime | 51 import datetime |
52 from email.MIMEMultipart import MIMEMultipart | 52 from email.MIMEMultipart import MIMEMultipart |
53 from email.MIMEBase import MIMEBase | 53 from email.MIMEBase import MIMEBase |
54 from email.MIMEText import MIMEText | 54 from email.MIMEText import MIMEText |
55 from email.Utils import formatdate | 55 from email.Utils import formatdate |
56 from email import Encoders | 56 from email import Encoders |
| 57 import gzip |
| 58 |
57 | 59 |
58 try: | 60 try: |
59 import hashlib | 61 import hashlib |
60 _hashfn = hashlib.sha512 | 62 _hashfn = hashlib.sha512 |
61 except ImportError: | 63 except ImportError: |
62 import md5 | 64 import md5 |
63 _hashfn = md5.md5 | 65 _hashfn = md5.md5 |
64 | 66 |
65 # List of Query String Arguments of Interest | 67 # List of Query String Arguments of Interest |
66 qsa_of_interest = ['acl', 'location', 'logging', 'partNumber', 'policy', | 68 qsa_of_interest = ['acl', 'location', 'logging', 'partNumber', 'policy', |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
142 return final_headers | 144 return final_headers |
143 | 145 |
144 def get_aws_metadata(headers, provider=None): | 146 def get_aws_metadata(headers, provider=None): |
145 if not provider: | 147 if not provider: |
146 provider = boto.provider.get_default() | 148 provider = boto.provider.get_default() |
147 metadata_prefix = provider.metadata_prefix | 149 metadata_prefix = provider.metadata_prefix |
148 metadata = {} | 150 metadata = {} |
149 for hkey in headers.keys(): | 151 for hkey in headers.keys(): |
150 if hkey.lower().startswith(metadata_prefix): | 152 if hkey.lower().startswith(metadata_prefix): |
151 val = urllib.unquote_plus(headers[hkey]) | 153 val = urllib.unquote_plus(headers[hkey]) |
152 metadata[hkey[len(metadata_prefix):]] = unicode(val, 'utf-8') | 154 try: |
| 155 metadata[hkey[len(metadata_prefix):]] = unicode(val, 'utf-8') |
| 156 except UnicodeDecodeError: |
| 157 metadata[hkey[len(metadata_prefix):]] = val |
153 del headers[hkey] | 158 del headers[hkey] |
154 return metadata | 159 return metadata |
155 | 160 |
156 def retry_url(url, retry_on_404=True): | 161 def retry_url(url, retry_on_404=True, num_retries=10): |
157 for i in range(0, 10): | 162 for i in range(0, num_retries): |
158 try: | 163 try: |
159 req = urllib2.Request(url) | 164 req = urllib2.Request(url) |
160 resp = urllib2.urlopen(req) | 165 resp = urllib2.urlopen(req) |
161 return resp.read() | 166 return resp.read() |
162 except urllib2.HTTPError, e: | 167 except urllib2.HTTPError, e: |
163 # in 2.6 you use getcode(), in 2.5 and earlier you use code | 168 # in 2.6 you use getcode(), in 2.5 and earlier you use code |
164 if hasattr(e, 'getcode'): | 169 if hasattr(e, 'getcode'): |
165 code = e.getcode() | 170 code = e.getcode() |
166 else: | 171 else: |
167 code = e.code | 172 code = e.code |
(...skipping 21 matching lines...) Expand all Loading... |
189 resource = field[0:p] + '/openssh-key' | 194 resource = field[0:p] + '/openssh-key' |
190 else: | 195 else: |
191 key = resource = field | 196 key = resource = field |
192 val = retry_url(url + resource) | 197 val = retry_url(url + resource) |
193 p = val.find('\n') | 198 p = val.find('\n') |
194 if p > 0: | 199 if p > 0: |
195 val = val.split('\n') | 200 val = val.split('\n') |
196 d[key] = val | 201 d[key] = val |
197 return d | 202 return d |
198 | 203 |
199 def get_instance_metadata(version='latest'): | 204 def get_instance_metadata(version='latest', url='http://169.254.169.254'): |
200 """ | 205 """ |
201 Returns the instance metadata as a nested Python dictionary. | 206 Returns the instance metadata as a nested Python dictionary. |
202 Simple values (e.g. local_hostname, hostname, etc.) will be | 207 Simple values (e.g. local_hostname, hostname, etc.) will be |
203 stored as string values. Values such as ancestor-ami-ids will | 208 stored as string values. Values such as ancestor-ami-ids will |
204 be stored in the dict as a list of string values. More complex | 209 be stored in the dict as a list of string values. More complex |
205 fields such as public-keys and will be stored as nested dicts. | 210 fields such as public-keys and will be stored as nested dicts. |
206 """ | 211 """ |
207 url = 'http://169.254.169.254/%s/meta-data/' % version | 212 return _get_instance_metadata('%s/%s/meta-data/' % (url, version)) |
208 return _get_instance_metadata(url) | |
209 | 213 |
210 def get_instance_userdata(version='latest', sep=None): | 214 def get_instance_userdata(version='latest', sep=None, |
211 url = 'http://169.254.169.254/%s/user-data' % version | 215 url='http://169.254.169.254'): |
212 user_data = retry_url(url, retry_on_404=False) | 216 ud_url = '%s/%s/user-data' % (url,version) |
| 217 user_data = retry_url(ud_url, retry_on_404=False) |
213 if user_data: | 218 if user_data: |
214 if sep: | 219 if sep: |
215 l = user_data.split(sep) | 220 l = user_data.split(sep) |
216 user_data = {} | 221 user_data = {} |
217 for nvpair in l: | 222 for nvpair in l: |
218 t = nvpair.split('=') | 223 t = nvpair.split('=') |
219 user_data[t[0].strip()] = t[1].strip() | 224 user_data[t[0].strip()] = t[1].strip() |
220 return user_data | 225 return user_data |
221 | 226 |
222 ISO8601 = '%Y-%m-%dT%H:%M:%SZ' | 227 ISO8601 = '%Y-%m-%dT%H:%M:%SZ' |
| 228 ISO8601_MS = '%Y-%m-%dT%H:%M:%S.%fZ' |
223 | 229 |
224 def get_ts(ts=None): | 230 def get_ts(ts=None): |
225 if not ts: | 231 if not ts: |
226 ts = time.gmtime() | 232 ts = time.gmtime() |
227 return time.strftime(ISO8601, ts) | 233 return time.strftime(ISO8601, ts) |
228 | 234 |
229 def parse_ts(ts): | 235 def parse_ts(ts): |
230 return datetime.datetime.strptime(ts, ISO8601) | 236 try: |
| 237 dt = datetime.datetime.strptime(ts, ISO8601) |
| 238 return dt |
| 239 except ValueError: |
| 240 dt = datetime.datetime.strptime(ts, ISO8601_MS) |
| 241 return dt |
231 | 242 |
232 def find_class(module_name, class_name=None): | 243 def find_class(module_name, class_name=None): |
233 if class_name: | 244 if class_name: |
234 module_name = "%s.%s" % (module_name, class_name) | 245 module_name = "%s.%s" % (module_name, class_name) |
235 modules = module_name.split('.') | 246 modules = module_name.split('.') |
236 c = None | 247 c = None |
237 | 248 |
238 try: | 249 try: |
239 for m in modules[1:]: | 250 for m in modules[1:]: |
240 if c: | 251 if c: |
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
495 item.next.previous = previous | 506 item.next.previous = previous |
496 else: | 507 else: |
497 self.tail = previous | 508 self.tail = previous |
498 | 509 |
499 item.previous = None | 510 item.previous = None |
500 item.next = self.head | 511 item.next = self.head |
501 self.head.previous = self.head = item | 512 self.head.previous = self.head = item |
502 | 513 |
503 class Password(object): | 514 class Password(object): |
504 """ | 515 """ |
505 Password object that stores itself as SHA512 hashed. | 516 Password object that stores itself as hashed. |
| 517 Hash defaults to SHA512 if available, MD5 otherwise. |
506 """ | 518 """ |
507 def __init__(self, str=None): | 519 hashfunc=_hashfn |
| 520 def __init__(self, str=None, hashfunc=None): |
508 """ | 521 """ |
509 Load the string from an initial value, this should be the raw SHA512 has
hed password | 522 Load the string from an initial value, this should be the raw hashed pas
sword. |
510 """ | 523 """ |
511 self.str = str | 524 self.str = str |
| 525 if hashfunc: |
| 526 self.hashfunc = hashfunc |
512 | 527 |
513 def set(self, value): | 528 def set(self, value): |
514 self.str = _hashfn(value).hexdigest() | 529 self.str = self.hashfunc(value).hexdigest() |
515 | 530 |
516 def __str__(self): | 531 def __str__(self): |
517 return str(self.str) | 532 return str(self.str) |
518 | 533 |
519 def __eq__(self, other): | 534 def __eq__(self, other): |
520 if other == None: | 535 if other == None: |
521 return False | 536 return False |
522 return str(_hashfn(other).hexdigest()) == str(self.str) | 537 return str(self.hashfunc(other).hexdigest()) == str(self.str) |
523 | 538 |
524 def __len__(self): | 539 def __len__(self): |
525 if self.str: | 540 if self.str: |
526 return len(self.str) | 541 return len(self.str) |
527 else: | 542 else: |
528 return 0 | 543 return 0 |
529 | 544 |
530 def notify(subject, body=None, html_body=None, to_string=None, attachments=[], a
ppend_instance_id=True): | 545 def notify(subject, body=None, html_body=None, to_string=None, attachments=None,
append_instance_id=True): |
| 546 attachments = attachments or [] |
531 if append_instance_id: | 547 if append_instance_id: |
532 subject = "[%s] %s" % (boto.config.get_value("Instance", "instance-id"),
subject) | 548 subject = "[%s] %s" % (boto.config.get_value("Instance", "instance-id"),
subject) |
533 if not to_string: | 549 if not to_string: |
534 to_string = boto.config.get_value('Notification', 'smtp_to', None) | 550 to_string = boto.config.get_value('Notification', 'smtp_to', None) |
535 if to_string: | 551 if to_string: |
536 try: | 552 try: |
537 from_string = boto.config.get_value('Notification', 'smtp_from', 'bo
to') | 553 from_string = boto.config.get_value('Notification', 'smtp_from', 'bo
to') |
538 msg = MIMEMultipart() | 554 msg = MIMEMultipart() |
539 msg['From'] = from_string | 555 msg['From'] = from_string |
540 msg['Reply-To'] = from_string | 556 msg['Reply-To'] = from_string |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
596 s = '' | 612 s = '' |
597 if name[0].isupper: | 613 if name[0].isupper: |
598 s = name[0].lower() | 614 s = name[0].lower() |
599 for c in name[1:]: | 615 for c in name[1:]: |
600 if c.isupper(): | 616 if c.isupper(): |
601 s += sep + c.lower() | 617 s += sep + c.lower() |
602 else: | 618 else: |
603 s += c | 619 s += c |
604 return s | 620 return s |
605 | 621 |
606 def awsify_name(name): | 622 def write_mime_multipart(content, compress=False, deftype='text/plain', delimite
r=':'): |
607 return name[0:1].upper()+name[1:] | 623 """Description: |
| 624 :param content: A list of tuples of name-content pairs. This is used |
| 625 instead of a dict to ensure that scripts run in order |
| 626 :type list of tuples: |
| 627 |
| 628 :param compress: Use gzip to compress the scripts, defaults to no compressio
n |
| 629 :type bool: |
| 630 |
| 631 :param deftype: The type that should be assumed if nothing else can be figur
ed out |
| 632 :type str: |
| 633 |
| 634 :param delimiter: mime delimiter |
| 635 :type str: |
| 636 |
| 637 :return: Final mime multipart |
| 638 :rtype: str: |
| 639 """ |
| 640 wrapper = MIMEMultipart() |
| 641 for name,con in content: |
| 642 definite_type = guess_mime_type(con, deftype) |
| 643 maintype, subtype = definite_type.split('/', 1) |
| 644 if maintype == 'text': |
| 645 mime_con = MIMEText(con, _subtype=subtype) |
| 646 else: |
| 647 mime_con = MIMEBase(maintype, subtype) |
| 648 mime_con.set_payload(con) |
| 649 # Encode the payload using Base64 |
| 650 Encoders.encode_base64(mime_con) |
| 651 mime_con.add_header('Content-Disposition', 'attachment', filename=name) |
| 652 wrapper.attach(mime_con) |
| 653 rcontent = wrapper.as_string() |
| 654 |
| 655 if compress: |
| 656 buf = StringIO.StringIO() |
| 657 gz = gzip.GzipFile(mode='wb', fileobj=buf) |
| 658 try: |
| 659 gz.write(rcontent) |
| 660 finally: |
| 661 gz.close() |
| 662 rcontent = buf.getvalue() |
| 663 |
| 664 return rcontent |
| 665 |
| 666 def guess_mime_type(content, deftype): |
| 667 """Description: Guess the mime type of a block of text |
| 668 :param content: content we're finding the type of |
| 669 :type str: |
| 670 |
| 671 :param deftype: Default mime type |
| 672 :type str: |
| 673 |
| 674 :rtype: <type>: |
| 675 :return: <description> |
| 676 """ |
| 677 #Mappings recognized by cloudinit |
| 678 starts_with_mappings={ |
| 679 '#include' : 'text/x-include-url', |
| 680 '#!' : 'text/x-shellscript', |
| 681 '#cloud-config' : 'text/cloud-config', |
| 682 '#upstart-job' : 'text/upstart-job', |
| 683 '#part-handler' : 'text/part-handler', |
| 684 '#cloud-boothook' : 'text/cloud-boothook' |
| 685 } |
| 686 rtype = deftype |
| 687 for possible_type,mimetype in starts_with_mappings.items(): |
| 688 if content.startswith(possible_type): |
| 689 rtype = mimetype |
| 690 break |
| 691 return(rtype) |
OLD | NEW |