diff --git a/changelogs/fragments/20240906-cloudwatch-log-metric-filter-unit-dimensions.yml b/changelogs/fragments/20240906-cloudwatch-log-metric-filter-unit-dimensions.yml new file mode 100644 index 0000000000..b2b721f288 --- /dev/null +++ b/changelogs/fragments/20240906-cloudwatch-log-metric-filter-unit-dimensions.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - cloudwatchlogs_log_group_metric_filter - Add support for ``unit`` and ``dimensions`` options (https://github.com/ansible-collections/amazon.aws/pull/XXX) diff --git a/plugins/modules/cloudwatchlogs_log_group_metric_filter.py b/plugins/modules/cloudwatchlogs_log_group_metric_filter.py index 8eddb671be..63a01bd24c 100644 --- a/plugins/modules/cloudwatchlogs_log_group_metric_filter.py +++ b/plugins/modules/cloudwatchlogs_log_group_metric_filter.py @@ -56,7 +56,19 @@ default_value: description: - The value to emit when a filter pattern does not match a log event. + - The I(default_value) and I(dimensions) options are mutually exclusive. type: float + unit: + description: + - The unit of the value. The various options are available `here `. + type: str + dimensions: + description: + - A dimension is a name/value pair that is a part of the identity of a metric. + - You can assign up to 3 dimensions to a metric. + - Dimensions are only supported for JSON or space-delimited metric filters. + - The I(default_value) and I(dimensions) options are mutually exclusive. + type: dict extends_documentation_fragment: - amazon.aws.common.modules - amazon.aws.region.modules @@ -74,12 +86,26 @@ metric_name: box_free_space metric_namespace: fluentd_metrics metric_value: "$.value" + unit: Bytes - name: delete metric filter on log group /fluentd/testcase amazon.aws.cloudwatchlogs_log_group_metric_filter: log_group_name: /fluentd/testcase filter_name: BoxFreeStorage state: absent + +- name: set metric filter on log group /fluentd/testcase with dimensions + amazon.aws.cloudwatchlogs_log_group_metric_filter: + log_group_name: /fluentd/testcase + filter_name: BoxFreeStorage + filter_pattern: '{($.value = *) && ($.hostname = *)}' + state: present + metric_transformation: + metric_name: box_free_space + metric_namespace: fluentd_metrics + metric_value: "$.value" + dimensions: + hostname: $.hostname """ RETURN = r""" @@ -106,24 +132,13 @@ def metricTransformationHandler(metricTransformations, originMetricTransformatio if originMetricTransformations: change = False originMetricTransformations = camel_dict_to_snake_dict(originMetricTransformations) - for item in ["default_value", "metric_name", "metric_namespace", "metric_value"]: + for item in ["default_value", "metric_name", "metric_namespace", "metric_value", "unit", "dimensions"]: if metricTransformations.get(item) != originMetricTransformations.get(item): change = True else: change = True - defaultValue = metricTransformations.get("default_value") - if isinstance(defaultValue, int) or isinstance(defaultValue, float): - retval = [ - { - "metricName": metricTransformations.get("metric_name"), - "metricNamespace": metricTransformations.get("metric_namespace"), - "metricValue": metricTransformations.get("metric_value"), - "defaultValue": defaultValue, - } - ] - else: - retval = [ + retval = [ { "metricName": metricTransformations.get("metric_name"), "metricNamespace": metricTransformations.get("metric_namespace"), @@ -131,6 +146,19 @@ def metricTransformationHandler(metricTransformations, originMetricTransformatio } ] + # Add optional values + defaultValue = metricTransformations.get("default_value") + if defaultValue is not None: + retval[0]["defaultValue"] = defaultValue + + dimensions = metricTransformations.get("dimensions") + if dimensions is not None: + retval[0]["dimensions"] = dimensions + + unit = metricTransformations.get("unit") + if unit is not None: + retval[0]["unit"] = unit + return retval, change @@ -147,6 +175,8 @@ def main(): metric_namespace=dict(type="str"), metric_value=dict(type="str"), default_value=dict(type="float"), + unit=dict(type="str"), + dimensions=dict(type="dict"), ), ), ) @@ -184,6 +214,10 @@ def main(): metricTransformation = [camel_dict_to_snake_dict(item) for item in [originMetricTransformations]] elif state == "present": + + if metric_transformation.get('default_value') is not None and metric_transformation.get('dimensions') is not None: + module.fail_json(msg="default_value and dimensions are mutually exclusive.") + metricTransformation, change = metricTransformationHandler( metricTransformations=metric_transformation, originMetricTransformations=originMetricTransformations ) diff --git a/tests/integration/targets/cloudwatchlogs/tasks/cloudwatchlogs_tests.yml b/tests/integration/targets/cloudwatchlogs/tasks/cloudwatchlogs_tests.yml index 9efdcc81ac..e127b76a13 100644 --- a/tests/integration/targets/cloudwatchlogs/tasks/cloudwatchlogs_tests.yml +++ b/tests/integration/targets/cloudwatchlogs/tasks/cloudwatchlogs_tests.yml @@ -100,6 +100,95 @@ - out is changed - out.metric_filters[0].metric_namespace == "made_with_ansible" + - name: Update metric transformation with dimensions on '{{ log_group_name }}' + amazon.aws.cloudwatchlogs_log_group_metric_filter: + log_group_name: "{{ log_group_name }}" + filter_name: "{{ filter_name }}" + filter_pattern: '{ ($.value = *) && ($.hostname = "box")}' + state: present + metric_transformation: + metric_name: box_free_space + metric_namespace: made_with_ansible + metric_value: $.value + dimensions: + hostname: $.hostname + register: out + + - name: Assert that metric_filter is configured with dimensions + ansible.builtin.assert: + that: + - out is changed + - out.metric_filters[0].metric_namespace == "made_with_ansible" + - out.metric_filters[0].dimensions.hostname == "$.hostname" + + - name: Update metric transformation with unit on '{{ log_group_name }}' + amazon.aws.cloudwatchlogs_log_group_metric_filter: + log_group_name: "{{ log_group_name }}" + filter_name: "{{ filter_name }}" + filter_pattern: '{ ($.value = *) && ($.hostname = "box")}' + state: present + metric_transformation: + metric_name: box_free_space + metric_namespace: made_with_ansible + metric_value: $.value + unit: Bytes + dimensions: + hostname: $.hostname + register: out + + - name: Assert that metric_filter is configured with dimensions and unit + ansible.builtin.assert: + that: + - out is changed + - out.metric_filters[0].metric_namespace == "made_with_ansible" + - out.metric_filters[0].dimensions.hostname == "$.hostname" + - out.metric_filters[0].unit == "Bytes" + + - name: Idempotency check on metric transformation on '{{ log_group_name }}' + amazon.aws.cloudwatchlogs_log_group_metric_filter: + log_group_name: "{{ log_group_name }}" + filter_name: "{{ filter_name }}" + filter_pattern: '{ ($.value = *) && ($.hostname = "box")}' + state: present + metric_transformation: + metric_name: box_free_space + metric_namespace: made_with_ansible + metric_value: $.value + unit: Bytes + dimensions: + hostname: $.hostname + register: out + + - name: Assert that idempotent action with unit and dimensions does not register as changed + ansible.builtin.assert: + that: + - out is not changed + - out.metric_filters[0].metric_namespace == "made_with_ansible" + - out.metric_filters[0].unit == "Bytes" + - out.metric_filters[0].dimensions.hostname == "$.hostname" + + - name: Update metric transformation with default_value and dimensions on '{{ log_group_name }}' + amazon.aws.cloudwatchlogs_log_group_metric_filter: + log_group_name: "{{ log_group_name }}" + filter_name: "{{ filter_name }}" + filter_pattern: '{ ($.value = *) && ($.hostname = "box")}' + state: present + metric_transformation: + metric_name: box_free_space + metric_namespace: made_with_ansible + metric_value: $.value + default_value: 3.1415 + dimensions: + hostname: $.hostname + register: out + ignore_errors: true + + - name: Update metric transformation with default_value and dimensions must fail + ansible.builtin.assert: + that: + - out is failed + - out.msg == "default_value and dimensions are mutually exclusive." + - name: checkmode delete metric filter on '{{ log_group_name }}' amazon.aws.cloudwatchlogs_log_group_metric_filter: log_group_name: "{{ log_group_name }}"