Skip to content

Commit

Permalink
cloudwatchlogs_log_group_metric_filter: add support for unit and dime…
Browse files Browse the repository at this point in the history
…nsions (#2286)

SUMMARY

This PR adds support for the options unit and dimensions in the cloudwatchlogs_log_group_metric_filter module.
This enables configuring unit and dimensions in Cloudwatch Logs Metricfilters using ansible, which was previously not possible.
The addition is pretty straigthforward since both unit and dimensions are part of the metric_transformation parameter.
dimensions and default_value are mutually exclusive, however:

The AWS API does not fail when both are present, instead the dimensions are simply ignored.
Since they are not top-level parameters, the ansible module option mutually_exclusive was not possible. Instead a custom check was added to the module that throws an error when both parameters are present.

An integration test has been added for this case, as well as for configuring metric_filters with units and/or dimensions.
The function metricTransformationHandler has been rewritten slightly due to the addition of the two extra optional parameters, to make it a bit more readable.
Happy to get your feedback!

ISSUE TYPE


Feature Pull Request

COMPONENT NAME

cloudwatchlogs_log_group_metric_filter
ADDITIONAL INFORMATION

Reviewed-by: GomathiselviS <[email protected]>
Reviewed-by: Jasper Misset
Reviewed-by: Alina Buzachis
  • Loading branch information
jmisset-cb authored Oct 24, 2024
1 parent 112615b commit 118905b
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -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/2286)
81 changes: 62 additions & 19 deletions plugins/modules/cloudwatchlogs_log_group_metric_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,22 @@
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 <https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html>`.
type: str
version_added: 8.3.0
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
version_added: 8.3.0
extends_documentation_fragment:
- amazon.aws.common.modules
- amazon.aws.region.modules
Expand All @@ -74,12 +89,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"""
Expand All @@ -92,7 +121,11 @@
"default_value": 3.1415,
"metric_name": "box_free_space",
"metric_namespace": "made_with_ansible",
"metric_value": "$.value"
"metric_value": "$.value",
"unit": "Bytes",
"dimensions": {
"hostname": "$.hostname"
}
}
]
"""
Expand All @@ -106,30 +139,32 @@ 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

retval = [
{
"metricName": metricTransformations.get("metric_name"),
"metricNamespace": metricTransformations.get("metric_namespace"),
"metricValue": metricTransformations.get("metric_value"),
}
]

# Add optional values
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 = [
{
"metricName": metricTransformations.get("metric_name"),
"metricNamespace": metricTransformations.get("metric_namespace"),
"metricValue": metricTransformations.get("metric_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

Expand All @@ -147,6 +182,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"),
),
),
)
Expand Down Expand Up @@ -184,6 +221,12 @@ 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
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}"
Expand Down

0 comments on commit 118905b

Please sign in to comment.