diff options
author | Zuul <zuul@review.opendev.org> | 2022-10-11 13:23:31 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2022-10-11 13:23:31 +0000 |
commit | 934333f06cbcadf726f020178e67b41c3691af64 (patch) | |
tree | ad607e1ac5d46154c87bec1ca2cdb8dea9db519b /ceilometer/polling | |
parent | 7a228d3a5f1d61a318d647ebd53c2d80a095c367 (diff) | |
parent | 225f1cd7765ddb7b725c538944947ada8c52e73f (diff) | |
download | ceilometer-934333f06cbcadf726f020178e67b41c3691af64.tar.gz |
Merge "Add response handlers to support different response types"
Diffstat (limited to 'ceilometer/polling')
-rw-r--r-- | ceilometer/polling/dynamic_pollster.py | 97 |
1 files changed, 90 insertions, 7 deletions
diff --git a/ceilometer/polling/dynamic_pollster.py b/ceilometer/polling/dynamic_pollster.py index 2e9ea4ac..8fd54a48 100644 --- a/ceilometer/polling/dynamic_pollster.py +++ b/ceilometer/polling/dynamic_pollster.py @@ -18,8 +18,10 @@ similar to the idea used for handling notifications. """ import copy +import json import re import time +import xmltodict from oslo_log import log @@ -46,6 +48,80 @@ def validate_sample_type(sample_type): % (sample_type, ceilometer_sample.TYPES)) +class XMLResponseHandler(object): + """This response handler converts an XML in string format to a dict""" + + @staticmethod + def handle(response): + return xmltodict.parse(response) + + +class JsonResponseHandler(object): + """This response handler converts a JSON in string format to a dict""" + + @staticmethod + def handle(response): + return json.loads(response) + + +class PlainTextResponseHandler(object): + """This response handler converts a string to a dict {'out'=<string>}""" + + @staticmethod + def handle(response): + return {'out': str(response)} + + +VALID_HANDLERS = { + 'json': JsonResponseHandler, + 'xml': XMLResponseHandler, + 'text': PlainTextResponseHandler +} + + +def validate_response_handler(val): + if not isinstance(val, list): + raise declarative.DynamicPollsterDefinitionException( + "Invalid response_handlers configuration. It must be a list. " + "Provided value type: %s" % type(val).__name__) + + for value in val: + if value not in VALID_HANDLERS: + raise declarative.DynamicPollsterDefinitionException( + "Invalid response_handler value [%s]. Accepted values " + "are [%s]" % (value, ', '.join(list(VALID_HANDLERS)))) + + +class ResponseHandlerChain(object): + """Tries to convert a string to a dict using the response handlers""" + + def __init__(self, response_handlers, **meta): + if not isinstance(response_handlers, list): + response_handlers = list(response_handlers) + + self.response_handlers = response_handlers + self.meta = meta + + def handle(self, response): + failed_handlers = [] + for handler in self.response_handlers: + try: + return handler.handle(response) + except Exception as e: + handler_name = handler.__name__ + failed_handlers.append(handler_name) + LOG.debug( + "Error handling response [%s] with handler [%s]: %s. " + "We will try the next one, if multiple handlers were " + "configured.", + response, handler_name, e) + + handlers_str = ', '.join(failed_handlers) + raise declarative.InvalidResponseTypeException( + "No remaining handlers to handle the response [%s], " + "used handlers [%s]. [%s]." % (response, handlers_str, self.meta)) + + class PollsterDefinitionBuilder(object): def __init__(self, definitions): @@ -440,7 +516,9 @@ class PollsterDefinitions(object): PollsterDefinition(name='timeout', default=30), PollsterDefinition(name='extra_metadata_fields_cache_seconds', default=3600), - PollsterDefinition(name='extra_metadata_fields') + PollsterDefinition(name='extra_metadata_fields'), + PollsterDefinition(name='response_handlers', default=['json'], + validator=validate_response_handler) ] extra_definitions = [] @@ -655,6 +733,11 @@ class PollsterSampleGatherer(object): def __init__(self, definitions): self.definitions = definitions + self.response_handler_chain = ResponseHandlerChain( + map(VALID_HANDLERS.get, + self.definitions.configurations['response_handlers']), + url_path=definitions.configurations['url_path'] + ) @property def default_discovery(self): @@ -668,17 +751,17 @@ class PollsterSampleGatherer(object): resp, url = self._internal_execute_request_get_samples( definitions=definitions, **kwargs) - response_json = resp.json() - entry_size = len(response_json) - LOG.debug("Entries [%s] in the JSON for request [%s] " + response_dict = self.response_handler_chain.handle(resp.text) + entry_size = len(response_dict) + LOG.debug("Entries [%s] in the DICT for request [%s] " "for dynamic pollster [%s].", - response_json, url, definitions['name']) + response_dict, url, definitions['name']) if entry_size > 0: samples = self.retrieve_entries_from_response( - response_json, definitions) + response_dict, definitions) url_to_next_sample = self.get_url_to_next_sample( - response_json, definitions) + response_dict, definitions) self.prepare_samples(definitions, samples, **kwargs) |