Update api docstrings to match guidelines

Per http://docs.openstack.org/developer/hacking/
and http://www.python.org/dev/peps/pep-0257/

Change-Id: I33f03d784996e298ebd94aabec93e5083b5df840
This commit is contained in:
Ziad Sawalha 2014-02-14 01:25:26 -06:00
parent 0becf6c08f
commit b14d12987b
5 changed files with 188 additions and 207 deletions

View File

@ -14,7 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
"""Heat API exception subclasses - maps API response errors to AWS Errors"""
"""Heat API exception subclasses - maps API response errors to AWS Errors."""
import six
import webob.exc
@ -24,12 +24,15 @@ from heat.common import serializers
class HeatAPIException(webob.exc.HTTPError):
'''
"""webob HTTPError subclass that creates a serialized body.
Subclass webob HTTPError so we can correctly serialize the wsgi response
into the http response body, using the format specified by the request.
Note this should not be used directly, instead use the subclasses
defined below which map to AWS API errors
'''
"""
code = 400
title = "HeatAPIException"
explanation = _("Generic HeatAPIException, please use specific "
@ -37,22 +40,22 @@ class HeatAPIException(webob.exc.HTTPError):
err_type = "Sender"
def __init__(self, detail=None):
'''
Overload HTTPError constructor, so we can create a default serialized
body. This is required because not all error responses are processed
by the wsgi controller (ie auth errors, which are further up the
paste pipeline. We serialize in XML by default (as AWS does)
'''
"""Overload HTTPError constructor to create a default serialized body.
This is required because not all error responses are processed
by the wsgi controller (such as auth errors), which are further up the
paste pipeline. We serialize in XML by default (as AWS does).
"""
webob.exc.HTTPError.__init__(self, detail=detail)
serializer = serializers.XMLResponseSerializer()
serializer.default(self, self.get_unserialized_body())
def get_unserialized_body(self):
'''
Return a dict suitable for serialization in the wsgi controller
"""Return a dict suitable for serialization in the wsgi controller.
This wraps the exception details in a format which maps to the
expected format for the AWS API
'''
expected format for the AWS API.
"""
# Note the aws response format specifies a "Code" element which is not
# the html response code, but the AWS API error code, e.g self.title
if self.detail:
@ -66,18 +69,18 @@ class HeatAPIException(webob.exc.HTTPError):
# Common Error Subclasses:
class HeatIncompleteSignatureError(HeatAPIException):
'''
The request signature does not conform to AWS standards
'''
"""The request signature does not conform to AWS standards."""
code = 400
title = "IncompleteSignature"
explanation = _("The request signature does not conform to AWS standards")
class HeatInternalFailureError(HeatAPIException):
'''
The request processing has failed due to some unknown error
'''
"""The request processing has failed due to some unknown error."""
code = 500
title = "InternalFailure"
explanation = _("The request processing has failed due to an "
@ -86,45 +89,45 @@ class HeatInternalFailureError(HeatAPIException):
class HeatInvalidActionError(HeatAPIException):
'''
The action or operation requested is invalid
'''
"""The action or operation requested is invalid."""
code = 400
title = "InvalidAction"
explanation = _("The action or operation requested is invalid")
class HeatInvalidClientTokenIdError(HeatAPIException):
'''
The X.509 certificate or AWS Access Key ID provided does not exist
'''
"""The X.509 certificate or AWS Access Key ID provided does not exist."""
code = 403
title = "InvalidClientTokenId"
explanation = _("The certificate or AWS Key ID provided does not exist")
class HeatInvalidParameterCombinationError(HeatAPIException):
'''
Parameters that must not be used together were used together
'''
"""Parameters that must not be used together were used together."""
code = 400
title = "InvalidParameterCombination"
explanation = _("Incompatible parameters were used together")
class HeatInvalidParameterValueError(HeatAPIException):
'''
A bad or out-of-range value was supplied for the input parameter
'''
"""A bad or out-of-range value was supplied for the input parameter."""
code = 400
title = "InvalidParameterValue"
explanation = _("A bad or out-of-range value was supplied")
class HeatInvalidQueryParameterError(HeatAPIException):
'''
AWS query string is malformed, does not adhere to AWS standards
'''
"""AWS query string is malformed, does not adhere to AWS standards."""
code = 400
title = "InvalidQueryParameter"
explanation = _("AWS query string is malformed, does not adhere to "
@ -132,46 +135,52 @@ class HeatInvalidQueryParameterError(HeatAPIException):
class HeatMalformedQueryStringError(HeatAPIException):
'''
The query string is malformed
'''
"""The query string is malformed."""
code = 404
title = "MalformedQueryString"
explanation = _("The query string is malformed")
class HeatMissingActionError(HeatAPIException):
'''
The request is missing an action or operation parameter
'''
"""The request is missing an action or operation parameter."""
code = 400
title = "MissingAction"
explanation = _("The request is missing an action or operation parameter")
class HeatMissingAuthenticationTokenError(HeatAPIException):
'''
"""Does not contain a valid AWS Access Key or certificate.
Request must contain either a valid (registered) AWS Access Key ID
or X.509 certificate
'''
or X.509 certificate.
"""
code = 403
title = "MissingAuthenticationToken"
explanation = _("Does not contain a valid AWS Access Key or certificate")
class HeatMissingParameterError(HeatAPIException):
'''
An input parameter that is mandatory for processing the request is missing
'''
"""A mandatory input parameter is missing.
An input parameter that is mandatory for processing the request is missing.
"""
code = 400
title = "MissingParameter"
explanation = _("A mandatory input parameter is missing")
class HeatOptInRequiredError(HeatAPIException):
'''
The AWS Access Key ID needs a subscription for the service
'''
"""The AWS Access Key ID needs a subscription for the service."""
code = 403
title = "OptInRequired"
explanation = _("The AWS Access Key ID needs a subscription for the "
@ -179,19 +188,22 @@ class HeatOptInRequiredError(HeatAPIException):
class HeatRequestExpiredError(HeatAPIException):
'''
"""Request expired or more than 15mins in the future.
Request is past expires date or the request date (either with 15 minute
padding), or the request date occurs more than 15 minutes in the future
'''
padding), or the request date occurs more than 15 minutes in the future.
"""
code = 400
title = "RequestExpired"
explanation = _("Request expired or more than 15mins in the future")
class HeatServiceUnavailableError(HeatAPIException):
'''
The request has failed due to a temporary failure of the server
'''
"""The request has failed due to a temporary failure of the server."""
code = 503
title = "ServiceUnavailable"
explanation = _("Service temporarily unavailable")
@ -199,18 +211,18 @@ class HeatServiceUnavailableError(HeatAPIException):
class HeatThrottlingError(HeatAPIException):
'''
Request was denied due to request throttling
'''
"""Request was denied due to request throttling."""
code = 400
title = "Throttling"
explanation = _("Request was denied due to request throttling")
class AlreadyExistsError(HeatAPIException):
'''
Resource with the name requested already exists
'''
"""Resource with the name requested already exists."""
code = 400
title = 'AlreadyExists'
explanation = _("Resource with the name requested already exists")
@ -218,20 +230,22 @@ class AlreadyExistsError(HeatAPIException):
# Not documented in the AWS docs, authentication failure errors
class HeatAccessDeniedError(HeatAPIException):
'''
"""Authentication fails due to user IAM group memberships.
This is the response given when authentication fails due to user
IAM group memberships meaning we deny access
'''
IAM group memberships meaning we deny access.
"""
code = 403
title = "AccessDenied"
explanation = _("User is not authorized to perform action")
class HeatSignatureError(HeatAPIException):
'''
This is the response given when authentication fails due to
a bad signature
'''
"""Authentication fails due to a bad signature."""
code = 403
title = "SignatureDoesNotMatch"
explanation = _("The request signature we calculated does not match the "
@ -240,9 +254,9 @@ class HeatSignatureError(HeatAPIException):
# Heat-specific errors
class HeatAPINotImplementedError(HeatAPIException):
'''
This is the response given when an API action is not yet implemented
'''
"""API action is not yet implemented."""
code = 500
title = "APINotImplemented"
explanation = _("The requested action is not yet implemented")
@ -250,9 +264,9 @@ class HeatAPINotImplementedError(HeatAPIException):
class HeatActionInProgressError(HeatAPIException):
'''
Cannot perform action on stack in its current state
'''
"""Cannot perform action on stack in its current state."""
code = 400
title = 'InvalidAction'
explanation = ("Cannot perform action on stack while other actions are " +
@ -260,49 +274,50 @@ class HeatActionInProgressError(HeatAPIException):
def map_remote_error(ex):
"""
Map RemoteError exceptions returned by the engine
to HeatAPIException subclasses which can be used to return
properly formatted AWS error responses
"""
inval_param_errors = (
'AttributeError',
'ValueError',
'InvalidTenant',
'StackNotFound',
'ResourceActionNotSupported',
'ResourceNotFound',
'ResourceNotAvailable',
'ResourceTypeNotFound',
'PhysicalResourceNotFound',
'WatchRuleNotFound',
'StackValidationFailed',
'InvalidSchemaError',
'InvalidTemplateReference',
'InvalidTemplateVersion',
'InvalidTemplateSection',
'UnknownUserParameter',
'UserParameterMissing',
'InvalidTemplateParameter',
'MissingCredentialError',
)
denied_errors = ('Forbidden', 'NotAuthorized')
already_exists_errors = ('StackExists')
invalid_action_errors = ('ActionInProgress',)
"""Map rpc_common.RemoteError exceptions to HeatAPIException subclasses.
ex_type = ex.__class__.__name__
Map rpc_common.RemoteError exceptions returned by the engine
to HeatAPIException subclasses which can be used to return
properly formatted AWS error responses.
"""
inval_param_errors = (
'AttributeError',
'ValueError',
'InvalidTenant',
'StackNotFound',
'ResourceActionNotSupported',
'ResourceNotFound',
'ResourceNotAvailable',
'ResourceTypeNotFound',
'PhysicalResourceNotFound',
'WatchRuleNotFound',
'StackValidationFailed',
'InvalidSchemaError',
'InvalidTemplateReference',
'InvalidTemplateVersion',
'InvalidTemplateSection',
'UnknownUserParameter',
'UserParameterMissing',
'InvalidTemplateParameter',
'MissingCredentialError',
)
denied_errors = ('Forbidden', 'NotAuthorized')
already_exists_errors = ('StackExists')
invalid_action_errors = ('ActionInProgress',)
if ex_type.endswith('_Remote'):
ex_type = ex_type[:-len('_Remote')]
ex_type = ex.__class__.__name__
if ex_type in inval_param_errors:
return HeatInvalidParameterValueError(detail=six.text_type(ex))
elif ex_type in denied_errors:
return HeatAccessDeniedError(detail=six.text_type(ex))
elif ex_type in already_exists_errors:
return AlreadyExistsError(detail=six.text_type(ex))
elif ex_type in invalid_action_errors:
return HeatActionInProgressError(detail=six.text_type(ex))
else:
# Map everything else to internal server error for now
return HeatInternalFailureError(detail=six.text_type(ex))
if ex_type.endswith('_Remote'):
ex_type = ex_type[:-len('_Remote')]
if ex_type in inval_param_errors:
return HeatInvalidParameterValueError(detail=six.text_type(ex))
elif ex_type in denied_errors:
return HeatAccessDeniedError(detail=six.text_type(ex))
elif ex_type in already_exists_errors:
return AlreadyExistsError(detail=six.text_type(ex))
elif ex_type in invalid_action_errors:
return HeatActionInProgressError(detail=six.text_type(ex))
else:
# Map everything else to internal server error for now
return HeatInternalFailureError(detail=six.text_type(ex))

View File

@ -11,9 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
'''
Helper utilities related to the AWS API implementations
'''
"""Helper utilities related to the AWS API implementations."""
import itertools
import re
@ -26,16 +24,12 @@ LOG = logging.getLogger(__name__)
def format_response(action, response):
"""
Format response from engine into API format
"""
"""Format response from engine into API format."""
return {'%sResponse' % action: {'%sResult' % action: response}}
def extract_param_pairs(params, prefix='', keyname='', valuename=''):
"""
Extract a dictionary of user input parameters, from AWS style
parameter-pair encoded list
"""Extract user input params from AWS style parameter-pair encoded list.
In the AWS API list items appear as two key-value
pairs (passed as query parameters) with keys of the form below:
@ -46,7 +40,7 @@ def extract_param_pairs(params, prefix='', keyname='', valuename=''):
Prefix.member.2.keyvalue=somevalue
We reformat this into a dict here to match the heat
engine API expected format
engine API expected format.
"""
plist = extract_param_list(params, prefix)
kvs = [(p[keyname], p[valuename]) for p in plist
@ -56,8 +50,7 @@ def extract_param_pairs(params, prefix='', keyname='', valuename=''):
def extract_param_list(params, prefix=''):
"""
Extract a list-of-dicts based on parameters containing AWS style list
"""Extract a list-of-dicts based on parameters containing AWS style list.
MetricData.member.1.MetricName=buffers
MetricData.member.1.Unit=Bytes
@ -67,9 +60,8 @@ def extract_param_list(params, prefix=''):
MetricData.member.2.Value=12345
This can be extracted by passing prefix=MetricData, resulting in a
list containing two dicts
list containing two dicts.
"""
key_re = re.compile(r"%s\.member\.([0-9]+)\.(.*)" % (prefix))
def get_param_data(params):
@ -94,10 +86,11 @@ def extract_param_list(params, prefix=''):
def get_param_value(params, key):
"""
"""Looks up an expected parameter in a parsed params dict.
Helper function, looks up an expected parameter in a parsed
params dict and returns the result. If params does not contain
the requested key we raise an exception of the appropriate type
the requested key we raise an exception of the appropriate type.
"""
try:
return params[key]
@ -107,9 +100,7 @@ def get_param_value(params, key):
def reformat_dict_keys(keymap=None, inputdict=None):
'''
Utility function for mapping one dict format to another
'''
"""Utility function for mapping one dict format to another."""
keymap = keymap or {}
inputdict = inputdict or {}
return dict([(outk, inputdict[ink]) for ink, outk in keymap.items()

View File

@ -11,9 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
Controller that returns information on the heat API versions
"""
"""Controller that returns information on the heat API versions."""
import httplib
import json
@ -23,9 +21,7 @@ import webob.dec
class Controller(object):
"""
A controller that produces information on the heat API versions.
"""
"""A controller that produces information on the heat API versions."""
def __init__(self, conf):
self.conf = conf

View File

@ -11,9 +11,8 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
endpoint for heat AWS-compatible CloudWatch API
"""
"""Endpoint for heat AWS-compatible CloudWatch API."""
from oslo import messaging
import six
@ -34,9 +33,9 @@ LOG = logging.getLogger(__name__)
class WatchController(object):
"""
WSGI controller for CloudWatch resource in heat API
Implements the API actions
"""WSGI controller for CloudWatch resource in heat API.
Implements the API actions.
"""
def __init__(self, options):
@ -61,10 +60,10 @@ class WatchController(object):
@staticmethod
def _reformat_dimensions(dims):
'''
Reformat dimensions list into AWS API format
Parameter dims is a list of dicts
'''
"""Reformat dimensions list into AWS API format.
:param dims: a list of dicts.
"""
newdims = []
for count, d in enumerate(dims, 1):
for key in d.keys():
@ -72,29 +71,21 @@ class WatchController(object):
return newdims
def delete_alarms(self, req):
"""
Implements DeleteAlarms API action
"""
"""Implements DeleteAlarms API action."""
self._enforce(req, 'DeleteAlarms')
return exception.HeatAPINotImplementedError()
def describe_alarm_history(self, req):
"""
Implements DescribeAlarmHistory API action
"""
"""Implements DescribeAlarmHistory API action."""
self._enforce(req, 'DescribeAlarmHistory')
return exception.HeatAPINotImplementedError()
def describe_alarms(self, req):
"""
Implements DescribeAlarms API action
"""
"""Implements DescribeAlarms API action."""
self._enforce(req, 'DescribeAlarms')
def format_metric_alarm(a):
"""
Reformat engine output into the AWS "MetricAlarm" format
"""
"""Reformat engine output into the AWS "MetricAlarm" format."""
keymap = {
engine_api.WATCH_ACTIONS_ENABLED: 'ActionsEnabled',
engine_api.WATCH_ALARM_ACTIONS: 'AlarmActions',
@ -118,7 +109,8 @@ class WatchController(object):
engine_api.WATCH_STATE_VALUE: 'StateValue',
engine_api.WATCH_STATISTIC: 'Statistic',
engine_api.WATCH_THRESHOLD: 'Threshold',
engine_api.WATCH_UNIT: 'Unit'}
engine_api.WATCH_UNIT: 'Unit',
}
# AWS doesn't return StackId in the main MetricAlarm
# structure, so we add StackId as a dimension to all responses
@ -151,47 +143,39 @@ class WatchController(object):
return result
def describe_alarms_for_metric(self, req):
"""
Implements DescribeAlarmsForMetric API action
"""
"""Implements DescribeAlarmsForMetric API action."""
self._enforce(req, 'DescribeAlarmsForMetric')
return exception.HeatAPINotImplementedError()
def disable_alarm_actions(self, req):
"""
Implements DisableAlarmActions API action
"""
"""Implements DisableAlarmActions API action."""
self._enforce(req, 'DisableAlarmActions')
return exception.HeatAPINotImplementedError()
def enable_alarm_actions(self, req):
"""
Implements EnableAlarmActions API action
"""
"""Implements EnableAlarmActions API action."""
self._enforce(req, 'EnableAlarmActions')
return exception.HeatAPINotImplementedError()
def get_metric_statistics(self, req):
"""
Implements GetMetricStatistics API action
"""
"""Implements GetMetricStatistics API action."""
self._enforce(req, 'GetMetricStatistics')
return exception.HeatAPINotImplementedError()
def list_metrics(self, req):
"""
Implements ListMetrics API action
"""Implements ListMetrics API action.
Lists metric datapoints associated with a particular alarm,
or all alarms if none specified
or all alarms if none specified.
"""
self._enforce(req, 'ListMetrics')
def format_metric_data(d, fil=None):
"""
Reformat engine output into the AWS "Metric" format
"""Reformat engine output into the AWS "Metric" format.
Takes an optional filter dict, which is traversed
so a metric dict is only returned if all keys match
the filter dict
the filter dict.
"""
fil = fil or {}
dimensions = [
@ -247,16 +231,12 @@ class WatchController(object):
return result
def put_metric_alarm(self, req):
"""
Implements PutMetricAlarm API action
"""
"""Implements PutMetricAlarm API action."""
self._enforce(req, 'PutMetricAlarm')
return exception.HeatAPINotImplementedError()
def put_metric_data(self, req):
"""
Implements PutMetricData API action
"""
"""Implements PutMetricData API action."""
self._enforce(req, 'PutMetricData')
con = req.context
@ -304,9 +284,7 @@ class WatchController(object):
return api_utils.format_response("PutMetricData", result)
def set_alarm_state(self, req):
"""
Implements SetAlarmState API action
"""
"""Implements SetAlarmState API action."""
self._enforce(req, 'SetAlarmState')
# Map from AWS state names to those used in the engine
@ -341,8 +319,6 @@ class WatchController(object):
def create_resource(options):
"""
Watch resource factory method.
"""
"""Watch resource factory method."""
deserializer = wsgi.JSONRequestDeserializer()
return wsgi.Resource(WatchController(options), deserializer)

View File

@ -21,10 +21,13 @@ from heat.common import identifier
def policy_enforce(handler):
'''
Decorator for a handler method that checks the path matches the
request context and enforce policy defined in policy.json
'''
"""Decorator that enforces policies.
Checks the path matches the request context and enforce policy defined in
policy.json.
This is a handler method decorator.
"""
@wraps(handler)
def handle_stack_method(controller, req, tenant_id, **kwargs):
if req.context.tenant_id != tenant_id:
@ -40,10 +43,10 @@ def policy_enforce(handler):
def identified_stack(handler):
'''
Decorator for a handler method that passes a stack identifier in place of
the various path components.
'''
"""Decorator that passes a stack identifier instead of path components.
This is a handler method decorator.
"""
@policy_enforce
@wraps(handler)
def handle_stack_method(controller, req, stack_name, stack_id, **kwargs):
@ -56,7 +59,7 @@ def identified_stack(handler):
def make_url(req, identity):
'''Return the URL for the supplied identity dictionary.'''
"""Return the URL for the supplied identity dictionary."""
try:
stack_identity = identifier.HeatIdentifier(**identity)
except ValueError:
@ -67,12 +70,12 @@ def make_url(req, identity):
def make_link(req, identity, relationship='self'):
'''Return a link structure for the supplied identity dictionary.'''
"""Return a link structure for the supplied identity dictionary."""
return {'href': make_url(req, identity), 'rel': relationship}
def get_allowed_params(params, whitelist):
'''Extract from ``params`` all entries listed in ``whitelist``
"""Extract from ``params`` all entries listed in ``whitelist``.
The returning dict will contain an entry for a key if, and only if,
there's an entry in ``whitelist`` for that key and at least one entry in
@ -83,7 +86,7 @@ def get_allowed_params(params, whitelist):
:param whitelist: an array of strings to whitelist
:returns: a dict with {key: value} pairs
'''
"""
allowed_params = {}
for key, get_type in six.iteritems(whitelist):