From a5fbf17743235d2fa5f7d5de77367ae953da450a Mon Sep 17 00:00:00 2001 From: Angus Salkeld Date: Fri, 16 Mar 2012 12:08:53 +1100 Subject: [PATCH] Remerge common code with glance. Signed-off-by: Angus Salkeld --- heat/api/middleware/version_negotiation.py | 3 -- heat/api/versions.py | 2 - heat/common/client.py | 46 ++++++++++++++++------ heat/common/config.py | 2 - heat/common/crypt.py | 2 - heat/common/exception.py | 21 ++++++++++ heat/common/utils.py | 9 ++--- 7 files changed, 59 insertions(+), 26 deletions(-) diff --git a/heat/api/middleware/version_negotiation.py b/heat/api/middleware/version_negotiation.py index 181d2788cb..a4efcc6057 100644 --- a/heat/api/middleware/version_negotiation.py +++ b/heat/api/middleware/version_negotiation.py @@ -22,9 +22,6 @@ return import logging import re -import routes - -from heat.api import v1 from heat.api import versions from heat.common import wsgi diff --git a/heat/api/versions.py b/heat/api/versions.py index b0498c9fd4..4625dbb713 100644 --- a/heat/api/versions.py +++ b/heat/api/versions.py @@ -22,8 +22,6 @@ import json import webob.dec -from heat.common import wsgi - class Controller(object): diff --git a/heat/common/client.py b/heat/common/client.py index e667ce2edf..a3a49d347f 100644 --- a/heat/common/client.py +++ b/heat/common/client.py @@ -127,10 +127,23 @@ class SendFileIterator: return self.len while self.sending: - sent = sendfile.sendfile(self.connection.sock.fileno(), - self.body.fileno(), - self.offset, - CHUNKSIZE) + try: + sent = sendfile.sendfile(self.connection.sock.fileno(), + self.body.fileno(), + self.offset, + CHUNKSIZE) + except OSError as e: + # suprisingly, sendfile may fail transiently instead of + # blocking, in which case we select on the socket in order + # to wait on its return to a writeable state before resuming + # the send loop + if e.errno in (errno.EAGAIN, errno.EBUSY): + wlist = [self.connection.sock.fileno()] + rfds, wfds, efds = select.select([], wlist, []) + if wfds: + continue + raise + self.sending = (sent != 0) self.offset += sent yield OfLength(sent) @@ -224,19 +237,19 @@ class BaseClient(object): key. If use_ssl is True, and this param is None (the default), then an environ variable - heat_CLIENT_KEY_FILE is looked for. If no such + HEAT_CLIENT_KEY_FILE is looked for. If no such environ variable is found, ClientConnectionError will be raised. :param cert_file: Optional PEM-formatted certificate chain file. If use_ssl is True, and this param is None (the default), then an environ variable - heat_CLIENT_CERT_FILE is looked for. If no such + HEAT_CLIENT_CERT_FILE is looked for. If no such environ variable is found, ClientConnectionError will be raised. :param ca_file: Optional CA cert file to use in SSL connections If use_ssl is True, and this param is None (the default), then an environ variable - heat_CLIENT_CA_FILE is looked for. + HEAT_CLIENT_CA_FILE is looked for. :param insecure: Optional. If set then the server's certificate will not be verified. """ @@ -263,11 +276,11 @@ class BaseClient(object): connect_kwargs = {} if self.use_ssl: if self.key_file is None: - self.key_file = os.environ.get('heat_CLIENT_KEY_FILE') + self.key_file = os.environ.get('HEAT_CLIENT_KEY_FILE') if self.cert_file is None: - self.cert_file = os.environ.get('heat_CLIENT_CERT_FILE') + self.cert_file = os.environ.get('HEAT_CLIENT_CERT_FILE') if self.ca_file is None: - self.ca_file = os.environ.get('heat_CLIENT_CA_FILE') + self.ca_file = os.environ.get('HEAT_CLIENT_CA_FILE') # Check that key_file/cert_file are either both set or both unset if self.cert_file is not None and self.key_file is None: @@ -275,7 +288,7 @@ class BaseClient(object): "and you have supplied a cert, " "however you have failed to supply either a " "key_file parameter or set the " - "heat_CLIENT_KEY_FILE environ variable") + "HEAT_CLIENT_KEY_FILE environ variable") raise exception.ClientConnectionError(msg) if self.key_file is not None and self.cert_file is None: @@ -283,7 +296,7 @@ class BaseClient(object): "and you have supplied a key, " "however you have failed to supply either a " "cert_file parameter or set the " - "heat_CLIENT_CERT_FILE environ variable") + "HEAT_CLIENT_CERT_FILE environ variable") raise exception.ClientConnectionError(msg) if (self.key_file is not None and @@ -506,6 +519,10 @@ class BaseClient(object): raise TypeError('Unsupported image type: %s' % body.__class__) res = c.getresponse() + + def _retry(res): + return res.getheader('Retry-After') + status_code = self.get_status_code(res) if status_code in self.OK_RESPONSE_CODES: return res @@ -523,8 +540,13 @@ class BaseClient(object): raise exception.Invalid(res.read()) elif status_code == httplib.MULTIPLE_CHOICES: raise exception.MultipleChoices(body=res.read()) + elif status_code == httplib.REQUEST_ENTITY_TOO_LARGE: + raise exception.LimitExceeded(retry=_retry(res), + body=res.read()) elif status_code == httplib.INTERNAL_SERVER_ERROR: raise Exception("Internal Server error: %s" % res.read()) + elif status_code == httplib.SERVICE_UNAVAILABLE: + raise exception.ServiceUnavailable(retry=_retry(res)) elif status_code == httplib.REQUEST_URI_TOO_LONG: raise exception.RequestUriTooLong(body=res.read()) else: diff --git a/heat/common/config.py b/heat/common/config.py index b412e1f8d3..e09381fd3c 100644 --- a/heat/common/config.py +++ b/heat/common/config.py @@ -168,8 +168,6 @@ def load_paste_app(conf, app_name=None): # Setup logging early setup_logging(conf) - logger = logging.getLogger(app_name) - app = wsgi.paste_deploy_app(conf_file, app_name, conf) # Log the options used when starting if we're in debug mode... diff --git a/heat/common/crypt.py b/heat/common/crypt.py index d34f49f9b9..f666fc0cfe 100644 --- a/heat/common/crypt.py +++ b/heat/common/crypt.py @@ -19,8 +19,6 @@ Routines for URL-safe encrypting/decrypting """ import base64 -import string -import os from Crypto.Cipher import AES from Crypto import Random diff --git a/heat/common/exception.py b/heat/common/exception.py index 8e4ef7a7ed..bf33872984 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -141,6 +141,27 @@ class MultipleChoices(HeatException): "means that you have not included a version indicator in a " "request URI.\n\nThe body of response returned:\n%(body)s") +class LimitExceeded(HeatException): + message = _("The request returned a 413 Request Entity Too Large. This " + "generally means that rate limiting or a quota threshold was " + "breached.\n\nThe response body:\n%(body)s") + + def __init__(self, *args, **kwargs): + self.retry_after = (int(kwargs['retry']) if kwargs.get('retry') + else None) + super(LimitExceeded, self).__init__(*args, **kwargs) + + +class ServiceUnavailable(HeatException): + message = _("The request returned a 503 ServiceUnavilable. This " + "generally occurs on service overload or other transient " + "outage.") + + def __init__(self, *args, **kwargs): + self.retry_after = (int(kwargs['retry']) if kwargs.get('retry') + else None) + super(ServiceUnavailable, self).__init__(*args, **kwargs) + class RequestUriTooLong(HeatException): message = _("The URI was too long.") diff --git a/heat/common/utils.py b/heat/common/utils.py index 1f8604d1ec..728d4f2d83 100644 --- a/heat/common/utils.py +++ b/heat/common/utils.py @@ -22,13 +22,10 @@ System-level utilities and helper functions. import datetime import errno -import inspect import logging import os import platform -import random import subprocess -import socket import sys import uuid @@ -327,9 +324,11 @@ def get_terminal_size(): if not height_width: try: p = subprocess.Popen(['stty', 'size'], - shell=false, + shell=False, stdout=subprocess.PIPE) - return tuple(int(x) for x in p.communicate()[0].split()) + result = p.communicate() + if p.returncode == 0: + return tuple(int(x) for x in result[0].split()) except: pass