diff options
-rw-r--r-- | ceilometer/polling/dynamic_pollster.py | 89 | ||||
-rw-r--r-- | ceilometer/tests/unit/polling/test_dynamic_pollster.py | 67 | ||||
-rw-r--r-- | doc/source/admin/telemetry-dynamic-pollster.rst | 10 |
3 files changed, 161 insertions, 5 deletions
diff --git a/ceilometer/polling/dynamic_pollster.py b/ceilometer/polling/dynamic_pollster.py index db183308..d3f8563e 100644 --- a/ceilometer/polling/dynamic_pollster.py +++ b/ceilometer/polling/dynamic_pollster.py @@ -319,10 +319,39 @@ class PollsterValueMapper(object): class PollsterDefinition(object): + """Represents a dynamic pollster configuration/parameter + + It abstract the job of developers when creating or extending parameters, + such as validating parameters name, values and so on. + """ def __init__(self, name, required=False, on_missing=lambda df: df.default, default=None, validation_regex=None, creatable=True, validator=None): + """Create a dynamic pollster configuration/parameter + + :param name: the name of the pollster parameter/configuration. + :param required: indicates if the configuration/parameter is + optional or not. + :param on_missing: function that is executed when the + parameter/configuration is missing. + :param default: the default value to be used. + :param validation_regex: the regular expression used to validate the + name of the configuration/parameter. + :param creatable: it is an override mechanism to avoid creating + a configuration/parameter with the default value. The default + is ``True``; therefore, we always use the default value. + However, we can disable the use of the default value by + setting ``False``. When we set this configuration to + ``False``, the parameter is not added to the definition + dictionary if not defined by the operator in the pollster + YAML configuration file. + :param validator: function used to validate the value of the + parameter/configuration when it is given by the user. This + function signature should receive a value that is the value of + the parameter to be validate. + """ + self.name = name self.required = required self.on_missing = on_missing @@ -370,7 +399,7 @@ class PollsterDefinitions(object): PollsterDefinition(name='user_id_attribute', default="user_id"), PollsterDefinition(name='resource_id_attribute', default="id"), PollsterDefinition(name='project_id_attribute', default="project_id"), - ] + PollsterDefinition(name='headers')] extra_definitions = [] @@ -553,11 +582,27 @@ class PollsterSampleGatherer(object): def internal_execute_request_get_samples(self, kwargs): keystone_client = kwargs['keystone_client'] url = self.get_request_linked_samples_url(kwargs) - resp = keystone_client.session.get(url, authenticated=True) + + request_arguments = self.create_request_arguments() + LOG.debug("Executing request against [url=%s] with parameters [" + "%s] for pollsters [%s]", url, request_arguments, + self.definitions.configurations["name"]) + + resp = keystone_client.session.get(url, **request_arguments) + if resp.status_code != requests.codes.ok: resp.raise_for_status() return resp, url + def create_request_arguments(self): + request_args = { + "authenticated": True + } + request_headers = self.definitions.configurations['headers'] + if request_headers: + request_args['headers'] = request_headers + return request_args + def get_request_linked_samples_url(self, kwargs): next_sample_url = kwargs.get('next_sample_url') if next_sample_url: @@ -633,9 +678,13 @@ class NonOpenStackApisSamplesGatherer(PollsterSampleGatherer): authenticator_arguments = list(map(str.strip, credentials.split(","))) authenticator_instance = authenticator_class(*authenticator_arguments) - resp = requests.get( - url, - auth=authenticator_instance) + request_arguments = self.create_request_arguments() + request_arguments["auth"] = authenticator_instance + + LOG.debug("Executing request against [url=%s] with parameters [" + "%s] for pollsters [%s]", url, request_arguments, + self.definitions.configurations["name"]) + resp = requests.get(url, **request_arguments) if resp.status_code != requests.codes.ok: raise declarative.NonOpenStackApisDynamicPollsterException( @@ -645,6 +694,36 @@ class NonOpenStackApisSamplesGatherer(PollsterSampleGatherer): return resp, url + def create_request_arguments(self): + request_arguments = super( + NonOpenStackApisSamplesGatherer, self).create_request_arguments() + + request_arguments.pop("authenticated") + + return request_arguments + + def execute_request_get_samples(self, **kwargs): + samples = super(NonOpenStackApisSamplesGatherer, + self).execute_request_get_samples(**kwargs) + + if samples: + user_id_attribute = self.definitions.configurations[ + 'user_id_attribute'] + project_id_attribute = self.definitions.configurations[ + 'project_id_attribute'] + resource_id_attribute = self.definitions.configurations[ + 'resource_id_attribute'] + + for request_sample in samples: + self.generate_new_attributes_in_sample( + request_sample, user_id_attribute, 'user_id') + self.generate_new_attributes_in_sample( + request_sample, project_id_attribute, 'project_id') + self.generate_new_attributes_in_sample( + request_sample, resource_id_attribute, 'id') + + return samples + def generate_new_attributes_in_sample( self, sample, attribute_key, new_attribute_key): if attribute_key: diff --git a/ceilometer/tests/unit/polling/test_dynamic_pollster.py b/ceilometer/tests/unit/polling/test_dynamic_pollster.py index aa73ca0f..c8652517 100644 --- a/ceilometer/tests/unit/polling/test_dynamic_pollster.py +++ b/ceilometer/tests/unit/polling/test_dynamic_pollster.py @@ -780,3 +780,70 @@ class TestDynamicPollster(base.BaseTestCase): returned_value = pollster.definitions.sample_extractor.\ retrieve_attribute_nested_value(json_object, key) self.assertEqual(0, returned_value) + + def test_create_request_arguments_NonOpenStackApisSamplesGatherer(self): + pollster_definition = { + 'name': "test-pollster", 'sample_type': "gauge", 'unit': "test", + 'value_attribute': "volume", + 'url_path': "https://test.com/v1/test/endpoint/fake", + "module": "someModule", + "authentication_object": "objectAuthentication", + "authentication_parameters": "authParam", + "headers": [{"header1": "val1"}, {"header2": "val2"}]} + + pollster = dynamic_pollster.DynamicPollster(pollster_definition) + + request_args = pollster.definitions.sample_gatherer\ + .create_request_arguments() + + self.assertTrue("headers" in request_args) + self.assertEqual(2, len(request_args["headers"])) + + self.assertEqual(['header1', 'header2'], + list(map(lambda h: list(h.keys())[0], + request_args["headers"]))) + + self.assertEqual(['val1', 'val2'], + list(map(lambda h: list(h.values())[0], + request_args["headers"]))) + + self.assertTrue("authenticated" not in request_args) + + def test_create_request_arguments_PollsterSampleGatherer(self): + pollster_definition = copy.deepcopy( + self.pollster_definition_only_required_fields) + pollster_definition["headers"] = [ + {"x-openstack-nova-api-version": "2.46"}, + {"custom_header": "custom"}, + {"some_other_header": "something"}] + + pollster = dynamic_pollster.DynamicPollster(pollster_definition) + + request_args = pollster.definitions.sample_gatherer\ + .create_request_arguments() + + self.assertTrue("headers" in request_args) + self.assertTrue("authenticated" in request_args) + self.assertTrue(request_args["authenticated"]) + + self.assertEqual(3, len(request_args["headers"])) + + self.assertEqual(['x-openstack-nova-api-version', 'custom_header', + "some_other_header"], + list(map(lambda h: list(h.keys())[0], + request_args["headers"]))) + + self.assertEqual(['2.46', 'custom', 'something'], + list(map(lambda h: list(h.values())[0], + request_args["headers"]))) + + def test_create_request_arguments_PollsterSampleGatherer_no_headers(self): + pollster = dynamic_pollster.DynamicPollster( + self.pollster_definition_only_required_fields) + + request_args =\ + pollster.definitions.sample_gatherer.create_request_arguments() + + self.assertTrue("headers" not in request_args) + self.assertTrue("authenticated" in request_args) + self.assertTrue(request_args["authenticated"]) diff --git a/doc/source/admin/telemetry-dynamic-pollster.rst b/doc/source/admin/telemetry-dynamic-pollster.rst index 6ed1faa2..62e6dc3e 100644 --- a/doc/source/admin/telemetry-dynamic-pollster.rst +++ b/doc/source/admin/telemetry-dynamic-pollster.rst @@ -171,6 +171,16 @@ attributes to define a dynamic pollster: ``response_entries_key`` elements that will be mapped to ``id`` attribute that is sent to Gnocchi. +* ``headers``: optional parameter. It is a map (similar to the + metadata_mapping) of key and value that can be used to customize the header + of the request that is executed against the URL. This configuration works + for both OpenStack and non-OpenStack dynamic pollster configuration. + + .. code-block:: yaml + + headers: + "x-openstack-nova-api-version": "2.46" + The complete YAML configuration to gather data from Magnum (that has been used as an example) is the following: |