Index: tools/telemetry/third_party/gsutil/third_party/boto/boto/utils.py |
diff --git a/tools/telemetry/third_party/gsutil/third_party/boto/boto/utils.py b/tools/telemetry/third_party/gsutil/third_party/boto/boto/utils.py |
deleted file mode 100644 |
index 0e7e3a79f37bb086094f1be810a97d23b57da018..0000000000000000000000000000000000000000 |
--- a/tools/telemetry/third_party/gsutil/third_party/boto/boto/utils.py |
+++ /dev/null |
@@ -1,1051 +0,0 @@ |
-# Copyright (c) 2006-2012 Mitch Garnaat http://garnaat.org/ |
-# Copyright (c) 2010, Eucalyptus Systems, Inc. |
-# Copyright (c) 2012 Amazon.com, Inc. or its affiliates. |
-# All rights reserved. |
-# |
-# Permission is hereby granted, free of charge, to any person obtaining a |
-# copy of this software and associated documentation files (the |
-# "Software"), to deal in the Software without restriction, including |
-# without limitation the rights to use, copy, modify, merge, publish, dis- |
-# tribute, sublicense, and/or sell copies of the Software, and to permit |
-# persons to whom the Software is furnished to do so, subject to the fol- |
-# lowing conditions: |
-# |
-# The above copyright notice and this permission notice shall be included |
-# in all copies or substantial portions of the Software. |
-# |
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
-# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
-# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
-# IN THE SOFTWARE. |
- |
-# |
-# Parts of this code were copied or derived from sample code supplied by AWS. |
-# The following notice applies to that code. |
-# |
-# This software code is made available "AS IS" without warranties of any |
-# kind. You may copy, display, modify and redistribute the software |
-# code either by itself or as incorporated into your code; provided that |
-# you do not remove any proprietary notices. Your use of this software |
-# code is at your own risk and you waive any claim against Amazon |
-# Digital Services, Inc. or its affiliates with respect to your use of |
-# this software code. (c) 2006 Amazon Digital Services, Inc. or its |
-# affiliates. |
- |
-""" |
-Some handy utility functions used by several classes. |
-""" |
- |
-import subprocess |
-import time |
-import logging.handlers |
-import boto |
-import boto.provider |
-import tempfile |
-import random |
-import smtplib |
-import datetime |
-import re |
-import email.mime.multipart |
-import email.mime.base |
-import email.mime.text |
-import email.utils |
-import email.encoders |
-import gzip |
-import threading |
-import locale |
-from boto.compat import six, StringIO, urllib, encodebytes |
- |
-from contextlib import contextmanager |
- |
-from hashlib import md5, sha512 |
-_hashfn = sha512 |
- |
-from boto.compat import json |
- |
-try: |
- from boto.compat.json import JSONDecodeError |
-except ImportError: |
- JSONDecodeError = ValueError |
- |
-# List of Query String Arguments of Interest |
-qsa_of_interest = ['acl', 'cors', 'defaultObjectAcl', 'location', 'logging', |
- 'partNumber', 'policy', 'requestPayment', 'torrent', |
- 'versioning', 'versionId', 'versions', 'website', |
- 'uploads', 'uploadId', 'response-content-type', |
- 'response-content-language', 'response-expires', |
- 'response-cache-control', 'response-content-disposition', |
- 'response-content-encoding', 'delete', 'lifecycle', |
- 'tagging', 'restore', |
- # storageClass is a QSA for buckets in Google Cloud Storage. |
- # (StorageClass is associated to individual keys in S3, but |
- # having it listed here should cause no problems because |
- # GET bucket?storageClass is not part of the S3 API.) |
- 'storageClass', |
- # websiteConfig is a QSA for buckets in Google Cloud |
- # Storage. |
- 'websiteConfig', |
- # compose is a QSA for objects in Google Cloud Storage. |
- 'compose'] |
- |
- |
-_first_cap_regex = re.compile('(.)([A-Z][a-z]+)') |
-_number_cap_regex = re.compile('([a-z])([0-9]+)') |
-_end_cap_regex = re.compile('([a-z0-9])([A-Z])') |
- |
- |
-def unquote_v(nv): |
- if len(nv) == 1: |
- return nv |
- else: |
- return (nv[0], urllib.parse.unquote(nv[1])) |
- |
- |
-def canonical_string(method, path, headers, expires=None, |
- provider=None): |
- """ |
- Generates the aws canonical string for the given parameters |
- """ |
- if not provider: |
- provider = boto.provider.get_default() |
- interesting_headers = {} |
- for key in headers: |
- lk = key.lower() |
- if headers[key] is not None and \ |
- (lk in ['content-md5', 'content-type', 'date'] or |
- lk.startswith(provider.header_prefix)): |
- interesting_headers[lk] = str(headers[key]).strip() |
- |
- # these keys get empty strings if they don't exist |
- if 'content-type' not in interesting_headers: |
- interesting_headers['content-type'] = '' |
- if 'content-md5' not in interesting_headers: |
- interesting_headers['content-md5'] = '' |
- |
- # just in case someone used this. it's not necessary in this lib. |
- if provider.date_header in interesting_headers: |
- interesting_headers['date'] = '' |
- |
- # if you're using expires for query string auth, then it trumps date |
- # (and provider.date_header) |
- if expires: |
- interesting_headers['date'] = str(expires) |
- |
- sorted_header_keys = sorted(interesting_headers.keys()) |
- |
- buf = "%s\n" % method |
- for key in sorted_header_keys: |
- val = interesting_headers[key] |
- if key.startswith(provider.header_prefix): |
- buf += "%s:%s\n" % (key, val) |
- else: |
- buf += "%s\n" % val |
- |
- # don't include anything after the first ? in the resource... |
- # unless it is one of the QSA of interest, defined above |
- t = path.split('?') |
- buf += t[0] |
- |
- if len(t) > 1: |
- qsa = t[1].split('&') |
- qsa = [a.split('=', 1) for a in qsa] |
- qsa = [unquote_v(a) for a in qsa if a[0] in qsa_of_interest] |
- if len(qsa) > 0: |
- qsa.sort(key=lambda x: x[0]) |
- qsa = ['='.join(a) for a in qsa] |
- buf += '?' |
- buf += '&'.join(qsa) |
- |
- return buf |
- |
- |
-def merge_meta(headers, metadata, provider=None): |
- if not provider: |
- provider = boto.provider.get_default() |
- metadata_prefix = provider.metadata_prefix |
- final_headers = headers.copy() |
- for k in metadata.keys(): |
- if k.lower() in boto.s3.key.Key.base_user_settable_fields: |
- final_headers[k] = metadata[k] |
- else: |
- final_headers[metadata_prefix + k] = metadata[k] |
- |
- return final_headers |
- |
- |
-def get_aws_metadata(headers, provider=None): |
- if not provider: |
- provider = boto.provider.get_default() |
- metadata_prefix = provider.metadata_prefix |
- metadata = {} |
- for hkey in headers.keys(): |
- if hkey.lower().startswith(metadata_prefix): |
- val = urllib.parse.unquote(headers[hkey]) |
- if isinstance(val, bytes): |
- try: |
- val = val.decode('utf-8') |
- except UnicodeDecodeError: |
- # Just leave the value as-is |
- pass |
- metadata[hkey[len(metadata_prefix):]] = val |
- del headers[hkey] |
- return metadata |
- |
- |
-def retry_url(url, retry_on_404=True, num_retries=10, timeout=None): |
- """ |
- Retry a url. This is specifically used for accessing the metadata |
- service on an instance. Since this address should never be proxied |
- (for security reasons), we create a ProxyHandler with a NULL |
- dictionary to override any proxy settings in the environment. |
- """ |
- for i in range(0, num_retries): |
- try: |
- proxy_handler = urllib.request.ProxyHandler({}) |
- opener = urllib.request.build_opener(proxy_handler) |
- req = urllib.request.Request(url) |
- r = opener.open(req, timeout=timeout) |
- result = r.read() |
- |
- if(not isinstance(result, six.string_types) and |
- hasattr(result, 'decode')): |
- result = result.decode('utf-8') |
- |
- return result |
- except urllib.error.HTTPError as e: |
- code = e.getcode() |
- if code == 404 and not retry_on_404: |
- return '' |
- except Exception as e: |
- pass |
- boto.log.exception('Caught exception reading instance data') |
- # If not on the last iteration of the loop then sleep. |
- if i + 1 != num_retries: |
- time.sleep(min(2 ** i, |
- boto.config.get('Boto', 'max_retry_delay', 60))) |
- boto.log.error('Unable to read instance data, giving up') |
- return '' |
- |
- |
-def _get_instance_metadata(url, num_retries, timeout=None): |
- return LazyLoadMetadata(url, num_retries, timeout) |
- |
- |
-class LazyLoadMetadata(dict): |
- def __init__(self, url, num_retries, timeout=None): |
- self._url = url |
- self._num_retries = num_retries |
- self._leaves = {} |
- self._dicts = [] |
- self._timeout = timeout |
- data = boto.utils.retry_url(self._url, num_retries=self._num_retries, timeout=self._timeout) |
- if data: |
- fields = data.split('\n') |
- for field in fields: |
- if field.endswith('/'): |
- key = field[0:-1] |
- self._dicts.append(key) |
- else: |
- p = field.find('=') |
- if p > 0: |
- key = field[p + 1:] |
- resource = field[0:p] + '/openssh-key' |
- else: |
- key = resource = field |
- self._leaves[key] = resource |
- self[key] = None |
- |
- def _materialize(self): |
- for key in self: |
- self[key] |
- |
- def __getitem__(self, key): |
- if key not in self: |
- # allow dict to throw the KeyError |
- return super(LazyLoadMetadata, self).__getitem__(key) |
- |
- # already loaded |
- val = super(LazyLoadMetadata, self).__getitem__(key) |
- if val is not None: |
- return val |
- |
- if key in self._leaves: |
- resource = self._leaves[key] |
- last_exception = None |
- |
- for i in range(0, self._num_retries): |
- try: |
- val = boto.utils.retry_url( |
- self._url + urllib.parse.quote(resource, |
- safe="/:"), |
- num_retries=self._num_retries, |
- timeout=self._timeout) |
- if val and val[0] == '{': |
- val = json.loads(val) |
- break |
- else: |
- p = val.find('\n') |
- if p > 0: |
- val = val.split('\n') |
- break |
- |
- except JSONDecodeError as e: |
- boto.log.debug( |
- "encountered '%s' exception: %s" % ( |
- e.__class__.__name__, e)) |
- boto.log.debug( |
- 'corrupted JSON data found: %s' % val) |
- last_exception = e |
- |
- except Exception as e: |
- boto.log.debug("encountered unretryable" + |
- " '%s' exception, re-raising" % ( |
- e.__class__.__name__)) |
- last_exception = e |
- raise |
- |
- boto.log.error("Caught exception reading meta data" + |
- " for the '%s' try" % (i + 1)) |
- |
- if i + 1 != self._num_retries: |
- next_sleep = min( |
- random.random() * 2 ** i, |
- boto.config.get('Boto', 'max_retry_delay', 60)) |
- time.sleep(next_sleep) |
- else: |
- boto.log.error('Unable to read meta data, giving up') |
- boto.log.error( |
- "encountered '%s' exception: %s" % ( |
- last_exception.__class__.__name__, last_exception)) |
- raise last_exception |
- |
- self[key] = val |
- elif key in self._dicts: |
- self[key] = LazyLoadMetadata(self._url + key + '/', |
- self._num_retries) |
- |
- return super(LazyLoadMetadata, self).__getitem__(key) |
- |
- def get(self, key, default=None): |
- try: |
- return self[key] |
- except KeyError: |
- return default |
- |
- def values(self): |
- self._materialize() |
- return super(LazyLoadMetadata, self).values() |
- |
- def items(self): |
- self._materialize() |
- return super(LazyLoadMetadata, self).items() |
- |
- def __str__(self): |
- self._materialize() |
- return super(LazyLoadMetadata, self).__str__() |
- |
- def __repr__(self): |
- self._materialize() |
- return super(LazyLoadMetadata, self).__repr__() |
- |
- |
-def _build_instance_metadata_url(url, version, path): |
- """ |
- Builds an EC2 metadata URL for fetching information about an instance. |
- |
- Example: |
- |
- >>> _build_instance_metadata_url('http://169.254.169.254', 'latest', 'meta-data/') |
- http://169.254.169.254/latest/meta-data/ |
- |
- :type url: string |
- :param url: URL to metadata service, e.g. 'http://169.254.169.254' |
- |
- :type version: string |
- :param version: Version of the metadata to get, e.g. 'latest' |
- |
- :type path: string |
- :param path: Path of the metadata to get, e.g. 'meta-data/'. If a trailing |
- slash is required it must be passed in with the path. |
- |
- :return: The full metadata URL |
- """ |
- return '%s/%s/%s' % (url, version, path) |
- |
- |
-def get_instance_metadata(version='latest', url='http://169.254.169.254', |
- data='meta-data/', timeout=None, num_retries=5): |
- """ |
- Returns the instance metadata as a nested Python dictionary. |
- Simple values (e.g. local_hostname, hostname, etc.) will be |
- stored as string values. Values such as ancestor-ami-ids will |
- be stored in the dict as a list of string values. More complex |
- fields such as public-keys and will be stored as nested dicts. |
- |
- If the timeout is specified, the connection to the specified url |
- will time out after the specified number of seconds. |
- |
- """ |
- try: |
- metadata_url = _build_instance_metadata_url(url, version, data) |
- return _get_instance_metadata(metadata_url, num_retries=num_retries, timeout=timeout) |
- except urllib.error.URLError: |
- return None |
- |
- |
-def get_instance_identity(version='latest', url='http://169.254.169.254', |
- timeout=None, num_retries=5): |
- """ |
- Returns the instance identity as a nested Python dictionary. |
- """ |
- iid = {} |
- base_url = _build_instance_metadata_url(url, version, |
- 'dynamic/instance-identity/') |
- try: |
- data = retry_url(base_url, num_retries=num_retries, timeout=timeout) |
- fields = data.split('\n') |
- for field in fields: |
- val = retry_url(base_url + '/' + field + '/', num_retries=num_retries, timeout=timeout) |
- if val[0] == '{': |
- val = json.loads(val) |
- if field: |
- iid[field] = val |
- return iid |
- except urllib.error.URLError: |
- return None |
- |
- |
-def get_instance_userdata(version='latest', sep=None, |
- url='http://169.254.169.254', timeout=None, num_retries=5): |
- ud_url = _build_instance_metadata_url(url, version, 'user-data') |
- user_data = retry_url(ud_url, retry_on_404=False, num_retries=num_retries, timeout=timeout) |
- if user_data: |
- if sep: |
- l = user_data.split(sep) |
- user_data = {} |
- for nvpair in l: |
- t = nvpair.split('=') |
- user_data[t[0].strip()] = t[1].strip() |
- return user_data |
- |
-ISO8601 = '%Y-%m-%dT%H:%M:%SZ' |
-ISO8601_MS = '%Y-%m-%dT%H:%M:%S.%fZ' |
-RFC1123 = '%a, %d %b %Y %H:%M:%S %Z' |
-LOCALE_LOCK = threading.Lock() |
- |
- |
-@contextmanager |
-def setlocale(name): |
- """ |
- A context manager to set the locale in a threadsafe manner. |
- """ |
- with LOCALE_LOCK: |
- saved = locale.setlocale(locale.LC_ALL) |
- |
- try: |
- yield locale.setlocale(locale.LC_ALL, name) |
- finally: |
- locale.setlocale(locale.LC_ALL, saved) |
- |
- |
-def get_ts(ts=None): |
- if not ts: |
- ts = time.gmtime() |
- return time.strftime(ISO8601, ts) |
- |
- |
-def parse_ts(ts): |
- with setlocale('C'): |
- ts = ts.strip() |
- try: |
- dt = datetime.datetime.strptime(ts, ISO8601) |
- return dt |
- except ValueError: |
- try: |
- dt = datetime.datetime.strptime(ts, ISO8601_MS) |
- return dt |
- except ValueError: |
- dt = datetime.datetime.strptime(ts, RFC1123) |
- return dt |
- |
- |
-def find_class(module_name, class_name=None): |
- if class_name: |
- module_name = "%s.%s" % (module_name, class_name) |
- modules = module_name.split('.') |
- c = None |
- |
- try: |
- for m in modules[1:]: |
- if c: |
- c = getattr(c, m) |
- else: |
- c = getattr(__import__(".".join(modules[0:-1])), m) |
- return c |
- except: |
- return None |
- |
- |
-def update_dme(username, password, dme_id, ip_address): |
- """ |
- Update your Dynamic DNS record with DNSMadeEasy.com |
- """ |
- dme_url = 'https://www.dnsmadeeasy.com/servlet/updateip' |
- dme_url += '?username=%s&password=%s&id=%s&ip=%s' |
- s = urllib.request.urlopen(dme_url % (username, password, dme_id, ip_address)) |
- return s.read() |
- |
- |
-def fetch_file(uri, file=None, username=None, password=None): |
- """ |
- Fetch a file based on the URI provided. |
- If you do not pass in a file pointer a tempfile.NamedTemporaryFile, |
- or None if the file could not be retrieved is returned. |
- The URI can be either an HTTP url, or "s3://bucket_name/key_name" |
- """ |
- boto.log.info('Fetching %s' % uri) |
- if file is None: |
- file = tempfile.NamedTemporaryFile() |
- try: |
- if uri.startswith('s3://'): |
- bucket_name, key_name = uri[len('s3://'):].split('/', 1) |
- c = boto.connect_s3(aws_access_key_id=username, |
- aws_secret_access_key=password) |
- bucket = c.get_bucket(bucket_name) |
- key = bucket.get_key(key_name) |
- key.get_contents_to_file(file) |
- else: |
- if username and password: |
- passman = urllib.request.HTTPPasswordMgrWithDefaultRealm() |
- passman.add_password(None, uri, username, password) |
- authhandler = urllib.request.HTTPBasicAuthHandler(passman) |
- opener = urllib.request.build_opener(authhandler) |
- urllib.request.install_opener(opener) |
- s = urllib.request.urlopen(uri) |
- file.write(s.read()) |
- file.seek(0) |
- except: |
- raise |
- boto.log.exception('Problem Retrieving file: %s' % uri) |
- file = None |
- return file |
- |
- |
-class ShellCommand(object): |
- |
- def __init__(self, command, wait=True, fail_fast=False, cwd=None): |
- self.exit_code = 0 |
- self.command = command |
- self.log_fp = StringIO() |
- self.wait = wait |
- self.fail_fast = fail_fast |
- self.run(cwd=cwd) |
- |
- def run(self, cwd=None): |
- boto.log.info('running:%s' % self.command) |
- self.process = subprocess.Popen(self.command, shell=True, |
- stdin=subprocess.PIPE, |
- stdout=subprocess.PIPE, |
- stderr=subprocess.PIPE, |
- cwd=cwd) |
- if(self.wait): |
- while self.process.poll() is None: |
- time.sleep(1) |
- t = self.process.communicate() |
- self.log_fp.write(t[0]) |
- self.log_fp.write(t[1]) |
- boto.log.info(self.log_fp.getvalue()) |
- self.exit_code = self.process.returncode |
- |
- if self.fail_fast and self.exit_code != 0: |
- raise Exception("Command " + self.command + |
- " failed with status " + self.exit_code) |
- |
- return self.exit_code |
- |
- def setReadOnly(self, value): |
- raise AttributeError |
- |
- def getStatus(self): |
- return self.exit_code |
- |
- status = property(getStatus, setReadOnly, None, |
- 'The exit code for the command') |
- |
- def getOutput(self): |
- return self.log_fp.getvalue() |
- |
- output = property(getOutput, setReadOnly, None, |
- 'The STDIN and STDERR output of the command') |
- |
- |
-class AuthSMTPHandler(logging.handlers.SMTPHandler): |
- """ |
- This class extends the SMTPHandler in the standard Python logging module |
- to accept a username and password on the constructor and to then use those |
- credentials to authenticate with the SMTP server. To use this, you could |
- add something like this in your boto config file: |
- |
- [handler_hand07] |
- class=boto.utils.AuthSMTPHandler |
- level=WARN |
- formatter=form07 |
- args=('localhost', 'username', 'password', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject') |
- """ |
- |
- def __init__(self, mailhost, username, password, |
- fromaddr, toaddrs, subject): |
- """ |
- Initialize the handler. |
- |
- We have extended the constructor to accept a username/password |
- for SMTP authentication. |
- """ |
- super(AuthSMTPHandler, self).__init__(mailhost, fromaddr, |
- toaddrs, subject) |
- self.username = username |
- self.password = password |
- |
- def emit(self, record): |
- """ |
- Emit a record. |
- |
- Format the record and send it to the specified addressees. |
- It would be really nice if I could add authorization to this class |
- without having to resort to cut and paste inheritance but, no. |
- """ |
- try: |
- port = self.mailport |
- if not port: |
- port = smtplib.SMTP_PORT |
- smtp = smtplib.SMTP(self.mailhost, port) |
- smtp.login(self.username, self.password) |
- msg = self.format(record) |
- msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % ( |
- self.fromaddr, |
- ','.join(self.toaddrs), |
- self.getSubject(record), |
- email.utils.formatdate(), msg) |
- smtp.sendmail(self.fromaddr, self.toaddrs, msg) |
- smtp.quit() |
- except (KeyboardInterrupt, SystemExit): |
- raise |
- except: |
- self.handleError(record) |
- |
- |
-class LRUCache(dict): |
- """A dictionary-like object that stores only a certain number of items, and |
- discards its least recently used item when full. |
- |
- >>> cache = LRUCache(3) |
- >>> cache['A'] = 0 |
- >>> cache['B'] = 1 |
- >>> cache['C'] = 2 |
- >>> len(cache) |
- 3 |
- |
- >>> cache['A'] |
- 0 |
- |
- Adding new items to the cache does not increase its size. Instead, the least |
- recently used item is dropped: |
- |
- >>> cache['D'] = 3 |
- >>> len(cache) |
- 3 |
- >>> 'B' in cache |
- False |
- |
- Iterating over the cache returns the keys, starting with the most recently |
- used: |
- |
- >>> for key in cache: |
- ... print key |
- D |
- A |
- C |
- |
- This code is based on the LRUCache class from Genshi which is based on |
- `Myghty <http://www.myghty.org>`_'s LRUCache from ``myghtyutils.util``, |
- written by Mike Bayer and released under the MIT license (Genshi uses the |
- BSD License). |
- """ |
- |
- class _Item(object): |
- def __init__(self, key, value): |
- self.previous = self.next = None |
- self.key = key |
- self.value = value |
- |
- def __repr__(self): |
- return repr(self.value) |
- |
- def __init__(self, capacity): |
- self._dict = dict() |
- self.capacity = capacity |
- self.head = None |
- self.tail = None |
- |
- def __contains__(self, key): |
- return key in self._dict |
- |
- def __iter__(self): |
- cur = self.head |
- while cur: |
- yield cur.key |
- cur = cur.next |
- |
- def __len__(self): |
- return len(self._dict) |
- |
- def __getitem__(self, key): |
- item = self._dict[key] |
- self._update_item(item) |
- return item.value |
- |
- def __setitem__(self, key, value): |
- item = self._dict.get(key) |
- if item is None: |
- item = self._Item(key, value) |
- self._dict[key] = item |
- self._insert_item(item) |
- else: |
- item.value = value |
- self._update_item(item) |
- self._manage_size() |
- |
- def __repr__(self): |
- return repr(self._dict) |
- |
- def _insert_item(self, item): |
- item.previous = None |
- item.next = self.head |
- if self.head is not None: |
- self.head.previous = item |
- else: |
- self.tail = item |
- self.head = item |
- self._manage_size() |
- |
- def _manage_size(self): |
- while len(self._dict) > self.capacity: |
- del self._dict[self.tail.key] |
- if self.tail != self.head: |
- self.tail = self.tail.previous |
- self.tail.next = None |
- else: |
- self.head = self.tail = None |
- |
- def _update_item(self, item): |
- if self.head == item: |
- return |
- |
- previous = item.previous |
- previous.next = item.next |
- if item.next is not None: |
- item.next.previous = previous |
- else: |
- self.tail = previous |
- |
- item.previous = None |
- item.next = self.head |
- self.head.previous = self.head = item |
- |
- |
-class Password(object): |
- """ |
- Password object that stores itself as hashed. |
- Hash defaults to SHA512 if available, MD5 otherwise. |
- """ |
- hashfunc = _hashfn |
- |
- def __init__(self, str=None, hashfunc=None): |
- """ |
- Load the string from an initial value, this should be the |
- raw hashed password. |
- """ |
- self.str = str |
- if hashfunc: |
- self.hashfunc = hashfunc |
- |
- def set(self, value): |
- if not isinstance(value, bytes): |
- value = value.encode('utf-8') |
- self.str = self.hashfunc(value).hexdigest() |
- |
- def __str__(self): |
- return str(self.str) |
- |
- def __eq__(self, other): |
- if other is None: |
- return False |
- if not isinstance(other, bytes): |
- other = other.encode('utf-8') |
- return str(self.hashfunc(other).hexdigest()) == str(self.str) |
- |
- def __len__(self): |
- if self.str: |
- return len(self.str) |
- else: |
- return 0 |
- |
- |
-def notify(subject, body=None, html_body=None, to_string=None, |
- attachments=None, append_instance_id=True): |
- attachments = attachments or [] |
- if append_instance_id: |
- subject = "[%s] %s" % ( |
- boto.config.get_value("Instance", "instance-id"), subject) |
- if not to_string: |
- to_string = boto.config.get_value('Notification', 'smtp_to', None) |
- if to_string: |
- try: |
- from_string = boto.config.get_value('Notification', |
- 'smtp_from', 'boto') |
- msg = email.mime.multipart.MIMEMultipart() |
- msg['From'] = from_string |
- msg['Reply-To'] = from_string |
- msg['To'] = to_string |
- msg['Date'] = email.utils.formatdate(localtime=True) |
- msg['Subject'] = subject |
- |
- if body: |
- msg.attach(email.mime.text.MIMEText(body)) |
- |
- if html_body: |
- part = email.mime.base.MIMEBase('text', 'html') |
- part.set_payload(html_body) |
- email.encoders.encode_base64(part) |
- msg.attach(part) |
- |
- for part in attachments: |
- msg.attach(part) |
- |
- smtp_host = boto.config.get_value('Notification', |
- 'smtp_host', 'localhost') |
- |
- # Alternate port support |
- if boto.config.get_value("Notification", "smtp_port"): |
- server = smtplib.SMTP(smtp_host, int( |
- boto.config.get_value("Notification", "smtp_port"))) |
- else: |
- server = smtplib.SMTP(smtp_host) |
- |
- # TLS support |
- if boto.config.getbool("Notification", "smtp_tls"): |
- server.ehlo() |
- server.starttls() |
- server.ehlo() |
- smtp_user = boto.config.get_value('Notification', 'smtp_user', '') |
- smtp_pass = boto.config.get_value('Notification', 'smtp_pass', '') |
- if smtp_user: |
- server.login(smtp_user, smtp_pass) |
- server.sendmail(from_string, to_string, msg.as_string()) |
- server.quit() |
- except: |
- boto.log.exception('notify failed') |
- |
- |
-def get_utf8_value(value): |
- if not six.PY2 and isinstance(value, bytes): |
- return value |
- |
- if not isinstance(value, six.string_types): |
- value = six.text_type(value) |
- |
- if isinstance(value, six.text_type): |
- value = value.encode('utf-8') |
- |
- return value |
- |
- |
-def mklist(value): |
- if not isinstance(value, list): |
- if isinstance(value, tuple): |
- value = list(value) |
- else: |
- value = [value] |
- return value |
- |
- |
-def pythonize_name(name): |
- """Convert camel case to a "pythonic" name. |
- |
- Examples:: |
- |
- pythonize_name('CamelCase') -> 'camel_case' |
- pythonize_name('already_pythonized') -> 'already_pythonized' |
- pythonize_name('HTTPRequest') -> 'http_request' |
- pythonize_name('HTTPStatus200Ok') -> 'http_status_200_ok' |
- pythonize_name('UPPER') -> 'upper' |
- pythonize_name('') -> '' |
- |
- """ |
- s1 = _first_cap_regex.sub(r'\1_\2', name) |
- s2 = _number_cap_regex.sub(r'\1_\2', s1) |
- return _end_cap_regex.sub(r'\1_\2', s2).lower() |
- |
- |
-def write_mime_multipart(content, compress=False, deftype='text/plain', delimiter=':'): |
- """Description: |
- :param content: A list of tuples of name-content pairs. This is used |
- instead of a dict to ensure that scripts run in order |
- :type list of tuples: |
- |
- :param compress: Use gzip to compress the scripts, defaults to no compression |
- :type bool: |
- |
- :param deftype: The type that should be assumed if nothing else can be figured out |
- :type str: |
- |
- :param delimiter: mime delimiter |
- :type str: |
- |
- :return: Final mime multipart |
- :rtype: str: |
- """ |
- wrapper = email.mime.multipart.MIMEMultipart() |
- for name, con in content: |
- definite_type = guess_mime_type(con, deftype) |
- maintype, subtype = definite_type.split('/', 1) |
- if maintype == 'text': |
- mime_con = email.mime.text.MIMEText(con, _subtype=subtype) |
- else: |
- mime_con = email.mime.base.MIMEBase(maintype, subtype) |
- mime_con.set_payload(con) |
- # Encode the payload using Base64 |
- email.encoders.encode_base64(mime_con) |
- mime_con.add_header('Content-Disposition', 'attachment', filename=name) |
- wrapper.attach(mime_con) |
- rcontent = wrapper.as_string() |
- |
- if compress: |
- buf = StringIO() |
- gz = gzip.GzipFile(mode='wb', fileobj=buf) |
- try: |
- gz.write(rcontent) |
- finally: |
- gz.close() |
- rcontent = buf.getvalue() |
- |
- return rcontent |
- |
- |
-def guess_mime_type(content, deftype): |
- """Description: Guess the mime type of a block of text |
- :param content: content we're finding the type of |
- :type str: |
- |
- :param deftype: Default mime type |
- :type str: |
- |
- :rtype: <type>: |
- :return: <description> |
- """ |
- # Mappings recognized by cloudinit |
- starts_with_mappings = { |
- '#include': 'text/x-include-url', |
- '#!': 'text/x-shellscript', |
- '#cloud-config': 'text/cloud-config', |
- '#upstart-job': 'text/upstart-job', |
- '#part-handler': 'text/part-handler', |
- '#cloud-boothook': 'text/cloud-boothook' |
- } |
- rtype = deftype |
- for possible_type, mimetype in starts_with_mappings.items(): |
- if content.startswith(possible_type): |
- rtype = mimetype |
- break |
- return(rtype) |
- |
- |
-def compute_md5(fp, buf_size=8192, size=None): |
- """ |
- Compute MD5 hash on passed file and return results in a tuple of values. |
- |
- :type fp: file |
- :param fp: File pointer to the file to MD5 hash. The file pointer |
- will be reset to its current location before the |
- method returns. |
- |
- :type buf_size: integer |
- :param buf_size: Number of bytes per read request. |
- |
- :type size: int |
- :param size: (optional) The Maximum number of bytes to read from |
- the file pointer (fp). This is useful when uploading |
- a file in multiple parts where the file is being |
- split inplace into different parts. Less bytes may |
- be available. |
- |
- :rtype: tuple |
- :return: A tuple containing the hex digest version of the MD5 hash |
- as the first element, the base64 encoded version of the |
- plain digest as the second element and the data size as |
- the third element. |
- """ |
- return compute_hash(fp, buf_size, size, hash_algorithm=md5) |
- |
- |
-def compute_hash(fp, buf_size=8192, size=None, hash_algorithm=md5): |
- hash_obj = hash_algorithm() |
- spos = fp.tell() |
- if size and size < buf_size: |
- s = fp.read(size) |
- else: |
- s = fp.read(buf_size) |
- while s: |
- if not isinstance(s, bytes): |
- s = s.encode('utf-8') |
- hash_obj.update(s) |
- if size: |
- size -= len(s) |
- if size <= 0: |
- break |
- if size and size < buf_size: |
- s = fp.read(size) |
- else: |
- s = fp.read(buf_size) |
- hex_digest = hash_obj.hexdigest() |
- base64_digest = encodebytes(hash_obj.digest()).decode('utf-8') |
- if base64_digest[-1] == '\n': |
- base64_digest = base64_digest[0:-1] |
- # data_size based on bytes read. |
- data_size = fp.tell() - spos |
- fp.seek(spos) |
- return (hex_digest, base64_digest, data_size) |
- |
- |
-def find_matching_headers(name, headers): |
- """ |
- Takes a specific header name and a dict of headers {"name": "value"}. |
- Returns a list of matching header names, case-insensitive. |
- |
- """ |
- return [h for h in headers if h.lower() == name.lower()] |
- |
- |
-def merge_headers_by_name(name, headers): |
- """ |
- Takes a specific header name and a dict of headers {"name": "value"}. |
- Returns a string of all header values, comma-separated, that match the |
- input header name, case-insensitive. |
- |
- """ |
- matching_headers = find_matching_headers(name, headers) |
- return ','.join(str(headers[h]) for h in matching_headers |
- if headers[h] is not None) |
- |
- |
-class RequestHook(object): |
- """ |
- This can be extended and supplied to the connection object |
- to gain access to request and response object after the request completes. |
- One use for this would be to implement some specific request logging. |
- """ |
- def handle_request_data(self, request, response, error=False): |
- pass |