aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--README.md109
-rw-r--r--defaults/main.yml9
-rw-r--r--meta/argument_specs.yml6
-rw-r--r--tasks/lineinfile.yml24
-rw-r--r--tasks/main.yml12
-rw-r--r--tasks/process_exporter.yml18
-rw-r--r--tasks/strategy_lineinfile.yml13
-rw-r--r--tasks/strategy_yaml.yml71
8 files changed, 231 insertions, 31 deletions
diff --git a/README.md b/README.md
index 090112f..6867f8d 100644
--- a/README.md
+++ b/README.md
@@ -55,10 +55,15 @@ prometheus_target_exporter_defaults: {}
# node_exporter:
# path: /opt/prometheus/targets.yml
# host: '{{ inventory_hostname }}:9100'
+ # labels: # Labels to match when using yaml strategy
+ # severity: warning
+ # job: external
# blackbox_exporter:
# path: /opt/targets/blackbox.yml
# host: 'https://{{ hostvars[inventory_hostname].ansible_host }}'
# path_prefix: ''
+ # labels:
+ # severity: critical
prometheus_target_exporter: []
```
@@ -97,7 +102,8 @@ The strategy decides how exactly targets are added to the targets file and more
importantly how to handle existing configuration.
* `lineinfile` is the default strategy and simply *appends* a line to the target
- file if it isn't already there.
+ file if it isn't already there. **Note:** Configured `labels` are ignored by
+ this strategy.
* `yaml` *parses* the yaml target file and adds the host to it. This might mess
with the readability of your yaml file, and you should avoid it if you edit
the yaml file manually as well.
@@ -131,6 +137,107 @@ So the target file should look something like this:
+ - host:9100
```
+`yaml` parses the YAML target file and adds the host to the target group that
+matches the specified labels. This strategy is useful when you have multiple
+target groups with different labels in a single file and want to automatically
+place hosts in the correct group.
+
+**Warning:** This strategy is destructive for comments and custom formatting. The
+file is parsed and regenerated on each run, which means any comments, blank lines
+between entries, or custom formatting will be lost. Only use this strategy if the
+target file is fully managed by Ansible.
+
+The `yaml` strategy:
+
+* Reads and parses the existing YAML target file
+* Finds the target group where labels **exactly match** the specified labels
+* Adds the host to that group's targets list (if not already present)
+* If no matching group exists, creates a new target group with the specified labels
+* If a host exists in a group with different labels, it is moved to the matching group
+* Creates the target file if it doesn't exist
+* Hosts without labels are matched to target groups without labels
+
+Labels can be specified in `prometheus_target_exporter_defaults` and/or
+`prometheus_target_exporter`. Labels from both are **merged**, with the
+exporter-level labels taking precedence over defaults.
+
+Example target file structure:
+
+```yaml
+- labels:
+ severity: warning
+ job: external
+ targets:
+ - host1.example.com:9100
+ - host2.example.com:9100
+
+- labels:
+ severity: critical
+ job: internal
+ targets:
+ - host3.example.com:9100
+```
+
+Example playbook using the `yaml` strategy:
+
+```yaml
+- name: Deploy node exporter with yaml strategy
+ hosts: myhost
+
+ vars:
+ prometheus_target_host: prometheus
+ prometheus_target_strategy: yaml
+ prometheus_target_exporter_defaults:
+ node_exporter:
+ path: /opt/prometheus/targets/node.yml
+ host: '{{ inventory_hostname }}:9100'
+ labels:
+ severity: warning
+ job: external
+ alert_group: node_exporters
+
+ roles:
+ - role: kliwniloc.prometheus_target
+ prometheus_target_exporter:
+ - id: node_exporter
+ # Override severity label for this specific exporter
+ - id: node_exporter
+ host: '{{ inventory_hostname }}:9115'
+ labels:
+ severity: critical # Merged with defaults: {severity: critical, job: external, alert_group: node_exporters}
+```
+
+After running the playbook on `myhost`, the target file `/opt/prometheus/targets/node.yml`
+will be updated from the example above to:
+
+```yaml
+- labels:
+ job: external
+ severity: warning
+ targets:
+ - host1.example.com:9100
+ - host2.example.com:9100
+ - myhost:9100
+
+- labels:
+ job: internal
+ severity: critical
+ targets:
+ - host3.example.com:9100
+
+- labels:
+ alert_group: node_exporters
+ job: external
+ severity: critical
+ targets:
+ - myhost:9115
+```
+
+The first exporter (`id: node_exporter`) was added to the existing group with
+matching labels (`severity: warning`, `job: external`). Since the defaults also
+include `alert_group: node_exporters`, but no existing group has all three labels,
+the second exporter created a new target group.
+
There are a few handlers that are notified if a new target is added. You will
want to use those to reload your Prometheus instance after adding modifying
targets. If you manage your target files in git you may also wish to commit the
diff --git a/defaults/main.yml b/defaults/main.yml
index 807f507..795e3cb 100644
--- a/defaults/main.yml
+++ b/defaults/main.yml
@@ -126,10 +126,15 @@ prometheus_target_exporter_defaults: {}
# node_exporter:
# path: /opt/prometheus/targets.yml
# host: '{{ inventory_hostname }}:9100'
+ # labels: # Labels to match when using yaml strategy
+ # severity: warning
+ # job: external
# blackbox_exporter:
# path: /opt/targets/blackbox.yml
# host: 'https://{{ hostvars[inventory_hostname].ansible_host }}'
# path_prefix: ''
+ # labels:
+ # severity: critical
# This is where you specify the exporters that should be deployed to prometheus.
# You should configure this on a per play basis. If you wish to configure
@@ -141,6 +146,10 @@ prometheus_target_exporter: []
# path: /path/to/targets/file/on/prometheus/host
# # overwrites: prometheus_target_exporter_defaults.node_exporter.host
# host: '{{ inventory_hostname }}:9100'
+ # # labels to match the target group in the YAML file (yaml strategy only)
+ # labels:
+ # severity: warning
+ # job: external
# This is a list of exporters that will be appended to the
# prometheus_target_exporter variable. duplicate exporters in
diff --git a/meta/argument_specs.yml b/meta/argument_specs.yml
index 9126bb8..1d960cf 100644
--- a/meta/argument_specs.yml
+++ b/meta/argument_specs.yml
@@ -24,7 +24,7 @@ argument_specs:
default: lineinfile
choices:
- lineinfile
- # More to be added
+ - yaml
prometheus_target_handler_command_enabled:
type: bool
@@ -91,6 +91,10 @@ argument_specs:
host:
type: str
required: false
+ labels:
+ type: dict
+ required: false
+ description: Labels to match the target group in the YAML file (yaml strategy only)
prometheus_target_default_exporters:
type: list
diff --git a/tasks/lineinfile.yml b/tasks/lineinfile.yml
deleted file mode 100644
index 8c60ba0..0000000
--- a/tasks/lineinfile.yml
+++ /dev/null
@@ -1,24 +0,0 @@
----
-- name: Make sure targets are deployed
- ansible.builtin.lineinfile:
- path: '{{ (
- item.path_prefix if item.path_prefix is defined else
- (prometheus_target_exporter_defaults[item.id].path_prefix
- | default(prometheus_target_exporter_target_prefix))
- if item.id is defined)
- ~
- (item.path if item.path is defined
- else prometheus_target_exporter_defaults[item.id].path if item.id is defined) | mandatory }}'
- line: '{{ prometheus_target_strategy_lineinfile_prefix ~
- (item.host if item.host is defined else prometheus_target_exporter_defaults[item.id].host) | mandatory ~
- prometheus_target_strategy_lineinfile_suffix }}'
- state: present
- become: true
- delegate_to: '{{ prometheus_target_host }}'
- loop: '{{ prometheus_target_exporter +
- ([] if prometheus_target_skip_default_exporters else prometheus_target_default_exporters) }}'
- register: lineinfile
-
-- name: Export fact
- ansible.builtin.set_fact:
- changed: '{{ changed or lineinfile.changed }}'
diff --git a/tasks/main.yml b/tasks/main.yml
index f8d6caa..5a184f8 100644
--- a/tasks/main.yml
+++ b/tasks/main.yml
@@ -1,11 +1,13 @@
---
-- name: Select strategy
+- name: Process all exporters
ansible.builtin.include_tasks:
- file: '{{ prometheus_target_strategy }}.yml'
- vars:
- changed: false
+ file: process_exporter.yml
+ loop: '{{ prometheus_target_exporter +
+ ([] if prometheus_target_skip_default_exporters else prometheus_target_default_exporters) }}'
+ loop_control:
+ loop_var: item
- name: Run handlers
ansible.builtin.include_tasks:
file: handlers.yml
- when: changed
+ when: changed | default(false)
diff --git a/tasks/process_exporter.yml b/tasks/process_exporter.yml
new file mode 100644
index 0000000..2d13b29
--- /dev/null
+++ b/tasks/process_exporter.yml
@@ -0,0 +1,18 @@
+---
+- name: Set exporter variables
+ vars:
+ _defaults: '{{ prometheus_target_exporter_defaults[item.id] | default({}) if item.id is defined else {} }}'
+ ansible.builtin.set_fact:
+ _target_path: >-
+ {{ (item.path_prefix if item.path_prefix is defined else
+ (_defaults.path_prefix | default(prometheus_target_exporter_target_prefix)))
+ ~
+ (item.path if item.path is defined else _defaults.path) | mandatory }}
+ _target_host: >-
+ {{ (item.host if item.host is defined else _defaults.host) | mandatory }}
+ _target_labels: >-
+ {{ (_defaults.labels | default({})) | combine(item.labels | default({})) }}
+
+- name: Execute strategy
+ ansible.builtin.include_tasks:
+ file: strategy_{{ prometheus_target_strategy }}.yml
diff --git a/tasks/strategy_lineinfile.yml b/tasks/strategy_lineinfile.yml
new file mode 100644
index 0000000..a5dd01f
--- /dev/null
+++ b/tasks/strategy_lineinfile.yml
@@ -0,0 +1,13 @@
+---
+- name: Deploy target via lineinfile
+ ansible.builtin.lineinfile:
+ path: '{{ _target_path }}'
+ line: '{{ prometheus_target_strategy_lineinfile_prefix ~ _target_host ~ prometheus_target_strategy_lineinfile_suffix }}'
+ state: present
+ delegate_to: '{{ prometheus_target_host }}'
+ become: true
+ register: _lineinfile_result
+
+- name: Track changes
+ ansible.builtin.set_fact:
+ changed: '{{ changed | default(false) or _lineinfile_result.changed }}'
diff --git a/tasks/strategy_yaml.yml b/tasks/strategy_yaml.yml
new file mode 100644
index 0000000..1d6df77
--- /dev/null
+++ b/tasks/strategy_yaml.yml
@@ -0,0 +1,71 @@
+---
+- name: Check if target file exists
+ ansible.builtin.stat:
+ path: '{{ _target_path }}'
+ register: _target_file_stat
+ delegate_to: '{{ prometheus_target_host }}'
+
+- name: Read target file
+ ansible.builtin.slurp:
+ src: '{{ _target_path }}'
+ register: _target_file_content
+ delegate_to: '{{ prometheus_target_host }}'
+ when: _target_file_stat.stat.exists
+
+- name: Parse YAML and add host to matching target group
+ ansible.builtin.set_fact:
+ _updated_targets: >-
+ {%- set targets = (_target_file_content.content | b64decode | from_yaml) if _target_file_stat.stat.exists else [] -%}
+ {%- set targets = targets if targets is not none else [] -%}
+ {%- set ns = namespace(found=false, result=[], match=true) -%}
+ {%- for group in targets -%}
+ {%- set group_labels = group.labels | default({}) -%}
+ {%- set ns.match = true -%}
+ {%- if _target_labels | length != group_labels | length -%}
+ {%- set ns.match = false -%}
+ {%- else -%}
+ {%- for key, value in _target_labels.items() -%}
+ {%- if group_labels.get(key) != value -%}
+ {%- set ns.match = false -%}
+ {%- endif -%}
+ {%- endfor -%}
+ {%- endif -%}
+ {%- if ns.match -%}
+ {%- if _target_host not in group.targets -%}
+ {%- set _ = group.targets.append(_target_host) -%}
+ {%- endif -%}
+ {%- set ns.found = true -%}
+ {%- set _ = ns.result.append(group) -%}
+ {%- else -%}
+ {%- set filtered_targets = group.targets | reject('equalto', _target_host) | list -%}
+ {%- if filtered_targets | length > 0 -%}
+ {%- if group_labels | length > 0 -%}
+ {%- set _ = ns.result.append({'labels': group_labels, 'targets': filtered_targets}) -%}
+ {%- else -%}
+ {%- set _ = ns.result.append({'targets': filtered_targets}) -%}
+ {%- endif -%}
+ {%- endif -%}
+ {%- endif -%}
+ {%- endfor -%}
+ {%- if not ns.found -%}
+ {%- if _target_labels | length > 0 -%}
+ {%- set new_group = {'labels': _target_labels, 'targets': [_target_host]} -%}
+ {%- else -%}
+ {%- set new_group = {'targets': [_target_host]} -%}
+ {%- endif -%}
+ {%- set _ = ns.result.append(new_group) -%}
+ {%- endif -%}
+ {{ ns.result }}
+
+- name: Write updated target file
+ ansible.builtin.copy:
+ content: '{{ _updated_targets | to_nice_yaml(indent=2, width=1337) }}'
+ dest: '{{ _target_path }}'
+ mode: '0644'
+ delegate_to: '{{ prometheus_target_host }}'
+ become: true
+ register: _yaml_result
+
+- name: Track changes
+ ansible.builtin.set_fact:
+ changed: '{{ changed | default(false) or _yaml_result.changed }}'