Merge branch 'master' into role-mkfs
This commit is contained in:
@@ -7,3 +7,6 @@ loop_var_prefix: "^(__|{role}_)"
|
||||
|
||||
exclude_paths:
|
||||
- .github/
|
||||
- .ansible/
|
||||
# ansible-lint thinks it's a playbook so gives an error, but its tasks are for include_task. decided to exclude it
|
||||
- roles/rustdesk/molecule/default/client-binary-existance.yml
|
||||
|
||||
4
.github/workflows/linters.yml
vendored
4
.github/workflows/linters.yml
vendored
@@ -26,6 +26,10 @@ jobs:
|
||||
# run: ansible-galaxy install -r requirements.yml
|
||||
# shell: micromamba-shell {0}
|
||||
|
||||
- name: "Install community.general collection"
|
||||
run: ansible-galaxy collection install community.general
|
||||
shell: micromamba-shell {0}
|
||||
|
||||
- name: Check workflow files themselves with ActionLint
|
||||
run: actionlint
|
||||
shell: micromamba-shell {0}
|
||||
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.ansible
|
||||
.vscode
|
||||
19
README.md
19
README.md
@@ -5,4 +5,23 @@
|
||||
|
||||
## Roles
|
||||
|
||||
- [alertmanager](roles/alertmanager/README.md)
|
||||
- [borgmatic](roles/borgmatic/README.md)
|
||||
- [curl_scheduled](roles/curl_scheduled/README.md)
|
||||
- [dnsmasq](roles/dnsmasq/README.md)
|
||||
- [grafana](roles/grafana/README.md)
|
||||
- [ipmi_exporter](roles/ipmi_exporter/README.md)
|
||||
- [karma](roles/karma/README.md)
|
||||
- [loki](roles/loki/README.md)
|
||||
- [mount_device](roles/mount_device/README.md)
|
||||
- [nginx](roles/nginx/README.md)
|
||||
- [node_exporter](roles/node_exporter/README.md)
|
||||
- [prometheus](roles/prometheus/README.md)
|
||||
- [promtail](roles/promtail/README.md)
|
||||
- [rclone_yandex](roles/rclone_yandex/README.md)
|
||||
- [rustdesk](roles/rustdesk/README.md)
|
||||
- [sftp_share](roles/sftp_share/README.md)
|
||||
- [smartctl_exporter](roles/smartctl_exporter/README.md)
|
||||
- [ufw](roles/ufw/README.md)
|
||||
- [wg_hub](roles/wg_hub/README.md)
|
||||
- [wg_spoke](roles/wg_spoke/README.md)
|
||||
@@ -1,10 +1,11 @@
|
||||
---
|
||||
namespace: genlab
|
||||
name: common
|
||||
version: 0.0.1
|
||||
version: 0.20.0
|
||||
readme: README.md
|
||||
authors:
|
||||
- Alexander Gorelyshev (corvus-migratorius@proton.me)
|
||||
- Sergey Malyuk (malyuk.ss@genlab.llc)
|
||||
description: >-
|
||||
The genlab.common collection includes roles for performing deploying and configuring common infrastructural components
|
||||
license_file: LICENSE
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
collections:
|
||||
- name: ansible.posix
|
||||
- name: community.general
|
||||
- name: community.grafana
|
||||
- name: community.docker
|
||||
- name: community.crypto
|
||||
- name: maxhoesel.borgbackup
|
||||
|
||||
53
roles/alertmanager/README.md
Normal file
53
roles/alertmanager/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
genlab.alertmanager
|
||||
=========
|
||||
|
||||
The [Alertmanager](https://github.com/prometheus/alertmanager) handles alerts sent by client applications such as the Prometheus server. It takes care of deduplicating, grouping, and routing them to the correct receiver integration such as email, PagerDuty, or OpsGenie. It also takes care of silencing and inhibition of alerts.
|
||||
|
||||
You'll need to manually specify the paths to your template files in the main Alertmanager configuration file (`alertmanager.yml`) on the target machine. The configuration file must have a `.yml` suffix, and template files must use a `.tmpl` suffix.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
```
|
||||
alertmanager_version: 0.28.1 # version of Alertmanager (AM)
|
||||
alertmanager_config_dir: "/etc/alertmanager/conf" # where to place AM configs
|
||||
alertmanager_storage_dir: "/var/lib/alertmanager" # path to AM storage
|
||||
alertmanager_dir: "/etc/alertmanager" # where to install AM on target
|
||||
alertmanager_user: "alertmanager" # system user name
|
||||
alertmanager_group: "alertmanager" # system group name
|
||||
config_source_dir: "./alertmanager" # path to config files on source
|
||||
|
||||
# Optional
|
||||
template_source_dir: "./templates" # path to template files on source
|
||||
```
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
```yaml
|
||||
roles:
|
||||
- role: genlab.template
|
||||
config_source_dir: alertmanager
|
||||
alertmanager_version: 0.28.1
|
||||
|
||||
```
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
corvus-migratorius@proton.me
|
||||
7
roles/alertmanager/defaults/main.yml
Normal file
7
roles/alertmanager/defaults/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
alertmanager_version: 0.28.1
|
||||
alertmanager_config_dir: "/etc/alertmanager/conf"
|
||||
alertmanager_storage_dir: "/var/lib/alertmanager"
|
||||
alertmanager_dir: "/etc/alertmanager"
|
||||
alertmanager_user: alertmanager
|
||||
alertmanager_group: alertmanager
|
||||
7
roles/alertmanager/handlers/main.yml
Normal file
7
roles/alertmanager/handlers/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: "(Re)start and enable Alertmanager"
|
||||
ansible.builtin.systemd_service:
|
||||
name: alertmanager.service
|
||||
state: restarted
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
17
roles/alertmanager/meta/main.yml
Normal file
17
roles/alertmanager/meta/main.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: alertmanager
|
||||
namespace: genlab
|
||||
author: "Alexander Gorelyshev"
|
||||
company: "Genlab, LLC"
|
||||
description: ""
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.1"
|
||||
|
||||
platforms:
|
||||
- name: "Ubuntu"
|
||||
versions: ["focal", "jammy", "noble"]
|
||||
|
||||
galaxy_tags: []
|
||||
|
||||
dependencies: []
|
||||
@@ -0,0 +1,36 @@
|
||||
global:
|
||||
# SMTP configuration for email notifications (if needed)
|
||||
smtp_smarthost: 'mailserver.example.com:587'
|
||||
smtp_from: 'alertmanager@example.com'
|
||||
smtp_auth_username: 'alertmanager'
|
||||
smtp_auth_password: 'your_password'
|
||||
|
||||
# Other global settings like resolve_timeout, http_config, etc.
|
||||
|
||||
route:
|
||||
# Default receiver for alerts
|
||||
receiver: 'default-receiver'
|
||||
|
||||
# Labels used for grouping alerts
|
||||
group_by: ['alertname', 'instance', 'severity']
|
||||
|
||||
# Timing settings (group_wait, group_interval, repeat_interval)
|
||||
# You can have nested 'routes' for more complex routing logic
|
||||
|
||||
receivers:
|
||||
- name: 'default-receiver'
|
||||
email_configs:
|
||||
- to: 'ops-team@example.com'
|
||||
|
||||
inhibit_rules:
|
||||
# Rules to suppress alerts based on other alerts
|
||||
# Example:
|
||||
- source_match:
|
||||
severity: 'critical'
|
||||
target_match:
|
||||
severity: 'warning'
|
||||
# Suppress 'warning' alerts if a 'critical' alert is also firing
|
||||
|
||||
templates:
|
||||
# Paths to template files for customizing notifications
|
||||
- '/etc/alertmanager/templates/*.tmpl'
|
||||
6
roles/alertmanager/molecule/default/converge.yml
Normal file
6
roles/alertmanager/molecule/default/converge.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
roles:
|
||||
- role: genlab.common.alertmanager
|
||||
config_source_dir: alertmanager
|
||||
27
roles/alertmanager/molecule/default/molecule.yml
Normal file
27
roles/alertmanager/molecule/default/molecule.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: ubuntu
|
||||
image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest
|
||||
pre_build_image: true
|
||||
command: ${MOLECULE_DOCKER_COMMAND:-""}
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:rw
|
||||
cgroupns_mode: host
|
||||
privileged: true
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
lint: |
|
||||
set -e
|
||||
yamllint .
|
||||
ansible-lint .
|
||||
55
roles/alertmanager/molecule/default/verify.yml
Normal file
55
roles/alertmanager/molecule/default/verify.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
any_errors_fatal: true
|
||||
|
||||
tasks:
|
||||
|
||||
- name: "Include default vars"
|
||||
ansible.builtin.include_vars:
|
||||
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/"
|
||||
extensions: ['yml']
|
||||
|
||||
- name: "Check if Alertmanager is installed"
|
||||
changed_when: false
|
||||
ansible.builtin.command: "alertmanager --version"
|
||||
register: alertmanager_installed_version
|
||||
|
||||
- name: "Check Alertmanager version"
|
||||
ansible.builtin.assert:
|
||||
that: "alertmanager_installed_version.stdout is regex('{{ alertmanager_version }}')"
|
||||
success_msg: "Alertmanager version {{ alertmanager_version }} is installed and working"
|
||||
fail_msg: "Alertmanager version {{ alertmanager_version }} is not installed or not working correctly"
|
||||
|
||||
# kics-scan ignore-block
|
||||
- name: "Check if Alertmanager is reachable"
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9093/-/healthy"
|
||||
return_content: true
|
||||
status_code: 200
|
||||
method: GET
|
||||
body_format: json
|
||||
register: alertmanager_health
|
||||
|
||||
- name: "Assert Alertmanager health status"
|
||||
ansible.builtin.assert:
|
||||
that: "alertmanager_health.content == 'OK'"
|
||||
success_msg: "Alertmanager is healthy"
|
||||
fail_msg: "Alertmanager is not healthy"
|
||||
|
||||
# kics-scan ignore-block
|
||||
- name: "Check if Alertmanager is ready"
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:9093/-/ready"
|
||||
return_content: true
|
||||
status_code: 200
|
||||
method: GET
|
||||
body_format: json
|
||||
register: alertmanager_ready
|
||||
|
||||
- name: "Assert Alertmanager readiness status"
|
||||
ansible.builtin.assert:
|
||||
that: "alertmanager_ready.content == 'OK'"
|
||||
success_msg: "Alertmanager is ready"
|
||||
fail_msg: "Alertmanager is not ready"
|
||||
24
roles/alertmanager/tasks/configuration.yml
Normal file
24
roles/alertmanager/tasks/configuration.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
|
||||
- name: "Copy config file"
|
||||
notify: "(Re)start and enable Alertmanager"
|
||||
ansible.builtin.template:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ alertmanager_config_dir }}/{{ item | basename }}"
|
||||
owner: "{{ alertmanager_user }}"
|
||||
group: "{{ alertmanager_group }}"
|
||||
mode: "0660"
|
||||
with_fileglob:
|
||||
- "{{ config_source_dir }}/*.yml"
|
||||
|
||||
- name: "Copy templates if existed"
|
||||
notify: "(Re)start and enable Alertmanager"
|
||||
when: template_source_dir is defined
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ alertmanager_config_dir }}/{{ item | basename }}"
|
||||
owner: "{{ alertmanager_user }}"
|
||||
group: "{{ alertmanager_group }}"
|
||||
mode: "0660"
|
||||
with_fileglob:
|
||||
- "{{ template_source_dir }}/*.tmpl"
|
||||
85
roles/alertmanager/tasks/install.yml
Normal file
85
roles/alertmanager/tasks/install.yml
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
- name: "Create Alertmanager system group"
|
||||
ansible.builtin.group:
|
||||
name: "{{ alertmanager_user }}"
|
||||
system: true
|
||||
state: present
|
||||
|
||||
- name: "Create Alertmanager system user"
|
||||
ansible.builtin.user:
|
||||
name: "{{ alertmanager_user }}"
|
||||
group: "{{ alertmanager_group }}"
|
||||
system: true
|
||||
shell: "/sbin/nologin"
|
||||
create_home: false
|
||||
state: present
|
||||
|
||||
- name: "Install Alertmanager from binary"
|
||||
block:
|
||||
- name: "Check Alertmanager version"
|
||||
changed_when: false
|
||||
ansible.builtin.command:
|
||||
cmd: "alertmanager --version"
|
||||
register: alertmanager_ver
|
||||
|
||||
- name: "Assert version correctness"
|
||||
ansible.builtin.assert:
|
||||
that: "alertmanager_ver.stdout is regex('{{ alertmanager_version }}')"
|
||||
success_msg: "alertmanager version {{ alertmanager_version }} is installed and working"
|
||||
fail_msg: "alertmanager version {{ alertmanager_version }} is not installed or not working correctly"
|
||||
|
||||
rescue:
|
||||
- name: "Create Alertmanager directories '{{ item }}'"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ alertmanager_user }}"
|
||||
group: "{{ alertmanager_group }}"
|
||||
mode: "0755"
|
||||
with_items:
|
||||
- "{{ alertmanager_config_dir }}"
|
||||
- "{{ alertmanager_dir }}"
|
||||
- "{{ alertmanager_storage_dir }}"
|
||||
|
||||
- name: "Download Alertmanager binary"
|
||||
ansible.builtin.get_url:
|
||||
url: "https://github.com/prometheus/alertmanager/releases/download/v{{ alertmanager_version }}/\
|
||||
alertmanager-{{ alertmanager_version }}.linux-amd64.tar.gz"
|
||||
dest: "/tmp/alertmanager-{{ alertmanager_version }}.linux-amd64.tar.gz"
|
||||
owner: "{{ alertmanager_user }}"
|
||||
group: "{{ alertmanager_group }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: "Unpack Alertmanager binaries"
|
||||
notify: "(Re)start and enable Alertmanager"
|
||||
ansible.builtin.unarchive:
|
||||
src: "/tmp/alertmanager-{{ alertmanager_version }}.linux-amd64.tar.gz"
|
||||
dest: "{{ alertmanager_dir }}"
|
||||
creates: "{{ alertmanager_dir }}/alertmanager-{{ alertmanager_version }}.linux-amd64"
|
||||
remote_src: true
|
||||
|
||||
- name: "Cleanup downloaded file"
|
||||
ansible.builtin.file:
|
||||
path: "/tmp/alertmanager-{{ alertmanager_version }}.linux-amd64.tar.gz"
|
||||
state: absent
|
||||
|
||||
- name: "Move official alertmanager and amtool binaries"
|
||||
ansible.builtin.copy:
|
||||
src: "{{ alertmanager_dir }}/alertmanager-{{ alertmanager_version }}.linux-amd64/{{ item }}"
|
||||
dest: "/usr/local/bin/{{ item }}"
|
||||
mode: "0755"
|
||||
owner: "{{ alertmanager_user }}"
|
||||
group: "{{ alertmanager_group }}"
|
||||
remote_src: true
|
||||
with_items:
|
||||
- alertmanager
|
||||
- amtool
|
||||
|
||||
|
||||
- name: "Create systemd service unit"
|
||||
ansible.builtin.template:
|
||||
src: alertmanager.service.j2
|
||||
dest: /etc/systemd/system/alertmanager.service
|
||||
owner: "{{ alertmanager_user }}"
|
||||
group: "{{ alertmanager_group }}"
|
||||
mode: "0660"
|
||||
9
roles/alertmanager/tasks/main.yml
Normal file
9
roles/alertmanager/tasks/main.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
- name: "Run installation tasks"
|
||||
ansible.builtin.include_tasks: install.yml
|
||||
|
||||
- name: "Run configuration tasks"
|
||||
ansible.builtin.include_tasks: configuration.yml
|
||||
|
||||
- name: "Flush handlers"
|
||||
ansible.builtin.meta: "flush_handlers"
|
||||
27
roles/alertmanager/templates/alertmanager.service.j2
Normal file
27
roles/alertmanager/templates/alertmanager.service.j2
Normal file
@@ -0,0 +1,27 @@
|
||||
[Unit]
|
||||
Description=Alertmanager Service
|
||||
After=network.target
|
||||
Documentation="https://prometheus.io/docs/alerting/latest/alertmanager/"
|
||||
|
||||
[Service]
|
||||
User={{ alertmanager_user }}
|
||||
Group={{ alertmanager_group }}
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/alertmanager \
|
||||
--config.file={{ alertmanager_config_dir }}/alertmanager.yml \
|
||||
--storage.path={{ alertmanager_storage_dir }}
|
||||
Restart=on-failure
|
||||
|
||||
# Security hardening
|
||||
ReadWritePaths={{ alertmanager_storage_dir }}
|
||||
ProtectSystem=strict
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectClock=yes
|
||||
RestrictSUIDSGID=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
1
roles/alertmanager/vars/main.yml
Normal file
1
roles/alertmanager/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
40
roles/borgmatic/README.md
Normal file
40
roles/borgmatic/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
ansible-borgmatic
|
||||
=========
|
||||
|
||||
This is a wrapper around the `borgmatic` role from the `maxhoesel.borgbackup` collection.
|
||||
|
||||
The wrapper solve the most outstading issue with the current implementation of the original role: inability to install latest (or arbitrary) versions of `borgmatic` and `borg`.
|
||||
|
||||
In the case of Borg we are fetching a release from Github.
|
||||
|
||||
In the case of Borgmatic we are installing it via `pipx`, as recommended by their official documentation found here: https://torsion.org/borgmatic/docs/how-to/set-up-backups/.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
- `maxhoesel.borgbackup` collection installed (see `requirements.yml`);
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
None
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
See `molecule/default/converge.yml`
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
corvus-migratorius@proton.me
|
||||
18
roles/borgmatic/defaults/main.yml
Normal file
18
roles/borgmatic/defaults/main.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
borgmatic_version: "1.4.0"
|
||||
borgmatic_glibc_version: "2.36"
|
||||
borgmatic_pipx_version: "1.7.1"
|
||||
borgmatic_binary_url: "\
|
||||
https://github.com/borgbackup/borg/releases/download/{{ borgmatic_version }}/borg-linux-glibc{{ borgmatic_glibc_version | replace('.', '') }}.tgz"
|
||||
borgmatic_pipx_bin_dir: "/opt/borgmatic/bin"
|
||||
borgmatic_schedule_oncalendar: "daily"
|
||||
borgmatic_push_pubkey: true
|
||||
borgmatic_sshkey_path: "/root/borgmatic/id_ed25519"
|
||||
borgmatic_compression: "lz4"
|
||||
borgmatic_keep_hourly: 0
|
||||
borgmatic_keep_daily: 3
|
||||
borgmatic_keep_weekly: 3
|
||||
borgmatic_keep_monthly: 1
|
||||
borgmatic_keep_yearly: 0
|
||||
borgmatic_uptime_kuma:
|
||||
borgmatic_loki:
|
||||
1
roles/borgmatic/handlers/main.yml
Normal file
1
roles/borgmatic/handlers/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
17
roles/borgmatic/meta/main.yml
Normal file
17
roles/borgmatic/meta/main.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: "borgmatic"
|
||||
namespace: genlab
|
||||
author: "Alexander Gorelyshev"
|
||||
company: "Genlab, LLC"
|
||||
description: ""
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.1"
|
||||
|
||||
platforms:
|
||||
- name: "Ubuntu"
|
||||
versions: ["focal", "jammy", "noble"]
|
||||
|
||||
galaxy_tags: []
|
||||
|
||||
dependencies: []
|
||||
48
roles/borgmatic/molecule/default/converge.yml
Normal file
48
roles/borgmatic/molecule/default/converge.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
vars:
|
||||
repo_path: "/home/borg/test-repo"
|
||||
|
||||
pre_tasks:
|
||||
- name: "Create a user for borg"
|
||||
ansible.builtin.user:
|
||||
name: borg
|
||||
shell: /bin/bash
|
||||
create_home: true
|
||||
|
||||
- name: "Generate test data file"
|
||||
ansible.builtin.copy:
|
||||
dest: "/tmp/data"
|
||||
content: "This is a test file!"
|
||||
owner: "{{ ansible_user_id }}"
|
||||
group: "{{ ansible_user_id }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: "Ensure the repo path exists"
|
||||
ansible.builtin.file:
|
||||
path: "{{ repo_path }}"
|
||||
state: directory
|
||||
owner: "borg"
|
||||
mode: "0700"
|
||||
|
||||
- name: "Install openssh-server"
|
||||
ansible.builtin.apt:
|
||||
name: openssh-server
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: "Start an SSH openssh-server"
|
||||
ansible.builtin.systemd:
|
||||
name: ssh
|
||||
state: started
|
||||
|
||||
roles:
|
||||
- role: genlab.common.borgmatic
|
||||
borgmatic_source_directories:
|
||||
- "/tmp/data"
|
||||
borgmatic_repo_path: "ssh://borg@localhost/./test-repo"
|
||||
borgmatic_repo_label: "test-repo"
|
||||
borgmatic_encryption_passphrase: "secret"
|
||||
repo_server_inventory_hostname: ubuntu # in production this should be an Ansible inventory hostname
|
||||
repo_server_user: borg
|
||||
31
roles/borgmatic/molecule/default/molecule.yml
Normal file
31
roles/borgmatic/molecule/default/molecule.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: ubuntu
|
||||
image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest
|
||||
pre_build_image: true
|
||||
command: ${MOLECULE_DOCKER_COMMAND:-""}
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:rw
|
||||
cgroupns_mode: host
|
||||
privileged: true
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
scenario:
|
||||
name: default
|
||||
test_sequence:
|
||||
- destroy
|
||||
- create
|
||||
- converge
|
||||
# - idempotence
|
||||
- verify
|
||||
48
roles/borgmatic/molecule/default/verify.yml
Normal file
48
roles/borgmatic/molecule/default/verify.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
any_errors_fatal: true
|
||||
|
||||
vars:
|
||||
repo_path: "/home/borg/test-repo"
|
||||
|
||||
tasks:
|
||||
- name: "Include default vars"
|
||||
ansible.builtin.include_vars:
|
||||
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/"
|
||||
extensions: ['yml']
|
||||
|
||||
- name: "Check if Borg is installed"
|
||||
changed_when: false
|
||||
register: borgmatic_installed_version
|
||||
ansible.builtin.command: "/usr/bin/borg --version"
|
||||
|
||||
- name: "Check Borg version"
|
||||
ansible.builtin.assert:
|
||||
that: borgmatic_installed_version.stdout.find(borgmatic_version)
|
||||
success_msg: "borg version {{ borgmatic_version }} is installed and working"
|
||||
fail_msg: "borg version {{ borgmatic_version }} is not installed or not working correctly"
|
||||
|
||||
- name: "Check if Borgmatic is installed"
|
||||
changed_when: false
|
||||
register: borgmatic_installed_version
|
||||
ansible.builtin.command:
|
||||
cmd: "/usr/bin/borgmatic --version"
|
||||
|
||||
- name: "Check that the test repo was created"
|
||||
register: borgmatic_test_repo_readme
|
||||
failed_when: borgmatic_test_repo_readme.stat.exists is false
|
||||
ansible.builtin.stat:
|
||||
path: "{{ repo_path }}"
|
||||
|
||||
- name: "Check that the systemd timer for Borgmatic is up and running"
|
||||
register: borgmatic_timer
|
||||
ansible.builtin.systemd:
|
||||
name: borgmatic.timer
|
||||
|
||||
- name: "Assert that the timer is running"
|
||||
ansible.builtin.assert:
|
||||
that: borgmatic_timer.status.ActiveState == "active"
|
||||
success_msg: "Timer is running"
|
||||
fail_msg: "Unexpected timer state: '{{ borgmatic_timer.status.ActiveState }}'"
|
||||
34
roles/borgmatic/tasks/config.yml
Normal file
34
roles/borgmatic/tasks/config.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
- name: "Compose basic configuration for Borgmatic"
|
||||
ansible.builtin.set_fact:
|
||||
borgmatic_composite_config:
|
||||
source_directories: "{{ borgmatic_source_directories }}"
|
||||
repositories:
|
||||
- path: "{{ borgmatic_repo_path }}"
|
||||
label: "{{ borgmatic_repo_label }}"
|
||||
encryption_passphrase: "{{ borgmatic_encryption_passphrase }}"
|
||||
compression: "{{ borgmatic_compression }}"
|
||||
# CLI output configuration
|
||||
list_details: true
|
||||
statistics: true
|
||||
exclude_caches: true
|
||||
# logging verbosity:
|
||||
verbosity: 1
|
||||
syslog_verbosity: 1
|
||||
monitoring_verbosity: 1
|
||||
# backup depth
|
||||
keep_hourly: "{{ borgmatic_keep_hourly }}"
|
||||
keep_daily: "{{ borgmatic_keep_daily }}"
|
||||
keep_weekly: "{{ borgmatic_keep_weekly }}"
|
||||
keep_monthly: "{{ borgmatic_keep_monthly }}"
|
||||
keep_yearly: "{{ borgmatic_keep_yearly }}"
|
||||
|
||||
- name: "Add Uptime Kuma configuration"
|
||||
when: borgmatic_uptime_kuma
|
||||
ansible.builtin.set_fact:
|
||||
borgmatic_composite_config: "{{ borgmatic_composite_config | combine({'uptime_kuma': borgmatic_uptime_kuma}) }}"
|
||||
|
||||
- name: "Add Loki configuration"
|
||||
when: borgmatic_loki
|
||||
ansible.builtin.set_fact:
|
||||
borgmatic_composite_config: "{{ borgmatic_composite_config | combine({'loki': borgmatic_loki}) }}"
|
||||
25
roles/borgmatic/tasks/handle-ssh-keys.yml
Normal file
25
roles/borgmatic/tasks/handle-ssh-keys.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
- name: "Ensure the path for SSH keys exists"
|
||||
ansible.builtin.file:
|
||||
path: "{{ borgmatic_sshkey_path | dirname }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
|
||||
- name: "Generate an ed25519 SSH key pair with 100 KDF rounds"
|
||||
register: borgmatic_ssh_key_pair
|
||||
community.crypto.openssh_keypair:
|
||||
type: ed25519
|
||||
path: "{{ borgmatic_sshkey_path }}"
|
||||
comment: "Generated by Ansible for Borgmatic"
|
||||
force: false
|
||||
mode: '0600'
|
||||
|
||||
- name: "Push the SSH key pair to the Borg repo host"
|
||||
when: borgmatic_push_pubkey
|
||||
delegate_to: "{{ repo_server_inventory_hostname }}"
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ repo_server_user }}"
|
||||
key: "{{ borgmatic_ssh_key_pair.public_key }}"
|
||||
state: present
|
||||
66
roles/borgmatic/tasks/install.yml
Normal file
66
roles/borgmatic/tasks/install.yml
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
- name: "Ensure that system dependencies are installed"
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- openssh-client
|
||||
- python3-pip
|
||||
- python3-venv
|
||||
state: present
|
||||
update_cache: true
|
||||
cache_valid_time: 3600
|
||||
|
||||
- name: "Install pipx"
|
||||
retries: 3
|
||||
delay: 1
|
||||
ansible.builtin.pip:
|
||||
name: "pipx=={{ borgmatic_pipx_version }}"
|
||||
executable: pip3
|
||||
break_system_packages: true
|
||||
|
||||
- name: "Ensure pipx binary is available in PATH"
|
||||
changed_when: false
|
||||
ansible.builtin.command:
|
||||
cmd: pipx ensurepath
|
||||
|
||||
- name: "Install borgmatic via pipx"
|
||||
retries: 3
|
||||
delay: 1
|
||||
environment:
|
||||
PIPX_BIN_DIR: "{{ borgmatic_pipx_bin_dir }}"
|
||||
community.general.pipx:
|
||||
name: borgmatic
|
||||
state: present
|
||||
install_deps: true
|
||||
|
||||
- name: "Install Borg if the correct version is not available"
|
||||
block:
|
||||
# we are looking for Borg installed in a directory that Max Hoesel's role exects to find it
|
||||
- name: "Get the currently installed version of Borg"
|
||||
changed_when: false
|
||||
register: borgmatic_version_installed
|
||||
ansible.builtin.command:
|
||||
cmd: /usr/bin/borg --version
|
||||
|
||||
- name: "Check that the correct version of Borg is installed"
|
||||
ansible.builtin.assert:
|
||||
that: borgmatic_version_installed.stdout.find(borgmatic_version)
|
||||
fail_msg: "The expected Borg version was not found: {{ borgmatic_version_installed }}"
|
||||
success_msg: "Found the expected Borg version ({{ borgmatic_version }})"
|
||||
|
||||
rescue:
|
||||
- name: "Download Borg from a custom URL: '{{ borgmatic_binary_url }}'"
|
||||
retries: 3
|
||||
delay: 1
|
||||
ansible.builtin.unarchive:
|
||||
src: "{{ borgmatic_binary_url }}"
|
||||
dest: "/opt/"
|
||||
remote_src: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: "Create a symbolic link for Borg"
|
||||
ansible.builtin.file:
|
||||
state: link
|
||||
src: "/opt/borg-dir/borg.exe"
|
||||
dest: "/usr/bin/borg"
|
||||
1
roles/borgmatic/tasks/integrations.yml
Normal file
1
roles/borgmatic/tasks/integrations.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
15
roles/borgmatic/tasks/main.yml
Normal file
15
roles/borgmatic/tasks/main.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: "Include tool installation tasks"
|
||||
ansible.builtin.include_tasks: "install.yml"
|
||||
|
||||
- name: "Include SSH key handling tasks"
|
||||
ansible.builtin.include_tasks: "handle-ssh-keys.yml"
|
||||
|
||||
- name: "Include configuration tasks"
|
||||
ansible.builtin.include_tasks: "config.yml"
|
||||
|
||||
- name: "Include tasks for third-party integrations"
|
||||
ansible.builtin.include_tasks: "integrations.yml"
|
||||
|
||||
- name: "Include tasks for running borgmatic"
|
||||
ansible.builtin.include_tasks: "run.yml"
|
||||
22
roles/borgmatic/tasks/run.yml
Normal file
22
roles/borgmatic/tasks/run.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
# A workaround for maxhoesel.borgbackup.borgmatic that does not support custom paths
|
||||
- name: "Create symbolic links for Borgmatic executables"
|
||||
loop:
|
||||
- borgmatic
|
||||
- generate-borgmatic-config
|
||||
- validate-borgmatic-config
|
||||
ansible.builtin.file:
|
||||
state: link
|
||||
src: "{{ borgmatic_pipx_bin_dir }}/{{ item }}"
|
||||
dest: /usr/bin/{{ item }}
|
||||
|
||||
- name: "Configure and run Borgmatic"
|
||||
ansible.builtin.include_role:
|
||||
name: maxhoesel.borgbackup.borgmatic
|
||||
vars:
|
||||
borgmatic_install: false # we handle installation separately to get the recent version
|
||||
# borgmatic_ssh_key_gen_options: "-t ed25519 -a 100"
|
||||
borgmatic_ssh_key_path: "{{ borgmatic_sshkey_path }}"
|
||||
borgmatic_schedule_on: "{{ borgmatic_schedule_oncalendar }}"
|
||||
borgmatic_schedule_max_random_delay: 600
|
||||
borgmatic_config: "{{ borgmatic_composite_config }}"
|
||||
1
roles/borgmatic/vars/main.yml
Normal file
1
roles/borgmatic/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
2
roles/curl_scheduled/.gitignore
vendored
Normal file
2
roles/curl_scheduled/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.vscode
|
||||
.idea
|
||||
41
roles/curl_scheduled/README.md
Normal file
41
roles/curl_scheduled/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
curl-scheduled
|
||||
=========
|
||||
|
||||
Configure curl to run on schedule by deploying a systemd service + timer. Useful for sending heartbeats.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
- `args`: arguments to the curl command
|
||||
|
||||
- `url`: address to be accessed by curl
|
||||
|
||||
- `schedule`: string compatible with systemd timer `OnSchedule` option (default: `minutely`)
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
See `molecule/default/converge.yml`.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
msayganova@genlab.llc
|
||||
|
||||
corvus-migratorius@proton.me
|
||||
4
roles/curl_scheduled/defaults/main.yml
Normal file
4
roles/curl_scheduled/defaults/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
curl_scheduled_curl_cmd: "/usr/bin/curl"
|
||||
curl_scheduled_curl_args: "-fsS -m 10"
|
||||
curl_scheduled_schedule: "minutely"
|
||||
1
roles/curl_scheduled/handlers/main.yml
Normal file
1
roles/curl_scheduled/handlers/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
17
roles/curl_scheduled/meta/main.yml
Normal file
17
roles/curl_scheduled/meta/main.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: curl_scheduled
|
||||
namespace: genlab
|
||||
author: "Alexander Gorelyshev"
|
||||
company: "Genlab, LLC"
|
||||
description: ""
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.1"
|
||||
|
||||
platforms:
|
||||
- name: "Ubuntu"
|
||||
versions: ["focal", "jammy", "noble"]
|
||||
|
||||
galaxy_tags: []
|
||||
|
||||
dependencies: []
|
||||
45
roles/curl_scheduled/molecule/default/converge.yml
Normal file
45
roles/curl_scheduled/molecule/default/converge.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
|
||||
pre_tasks:
|
||||
- name: "Create test directory"
|
||||
ansible.builtin.file:
|
||||
path: "/test"
|
||||
state: directory
|
||||
mode: "0664"
|
||||
|
||||
- name: "Create a test file"
|
||||
ansible.builtin.lineinfile:
|
||||
path: "/test/index.html"
|
||||
create: true
|
||||
mode: "0664"
|
||||
line: "OK"
|
||||
|
||||
- name: "Simulate remote HTTP server(s) directly on localhost"
|
||||
changed_when: false
|
||||
async: 1
|
||||
poll: 0
|
||||
args:
|
||||
chdir: "/test"
|
||||
loop:
|
||||
- 8080
|
||||
- 8081
|
||||
- 8082
|
||||
ansible.builtin.shell:
|
||||
cmd: nohup python3 -m http.server {{ item }} </dev/null >/dev/null 2>&1 &
|
||||
executable: /bin/bash
|
||||
|
||||
roles:
|
||||
- role: genlab.common.curl_scheduled
|
||||
services:
|
||||
- label: "localhost-test-zero"
|
||||
url: "http://127.0.0.1:8080"
|
||||
schedule: minutely
|
||||
- label: "localhost-test-one"
|
||||
url: "http://127.0.0.1:8081"
|
||||
schedule: hourly
|
||||
- label: "localhost-test-chained-curl"
|
||||
curl_cmd: '/usr/bin/curl "http://127.0.0.1:8081" && /usr/bin/curl'
|
||||
url: "http://127.0.0.1:8082"
|
||||
schedule: minutely
|
||||
27
roles/curl_scheduled/molecule/default/molecule.yml
Normal file
27
roles/curl_scheduled/molecule/default/molecule.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: ubuntu
|
||||
image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest
|
||||
pre_build_image: true
|
||||
command: ${MOLECULE_DOCKER_COMMAND:-""}
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:rw
|
||||
cgroupns_mode: host
|
||||
privileged: true
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
lint: |
|
||||
set -e
|
||||
yamllint .
|
||||
ansible-lint .
|
||||
81
roles/curl_scheduled/molecule/default/verify.yml
Normal file
81
roles/curl_scheduled/molecule/default/verify.yml
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
# Verify the systemd timers
|
||||
|
||||
- name: "Check the timer status - localhost-test-zero"
|
||||
register: curl_scheduled_timer_zero
|
||||
ansible.builtin.systemd:
|
||||
name: "curl-localhost-test-zero.timer"
|
||||
|
||||
- name: "Assert that the localhost-test-zero timer is running"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- curl_scheduled_timer_zero.status.ActiveState == "active"
|
||||
success_msg: "Timer is running"
|
||||
fail_msg: "Unexpected timer state: '{{ curl_scheduled_timer_zero.status.ActiveState }}'"
|
||||
|
||||
- name: "Check the timer status - localhost-test-one"
|
||||
register: curl_scheduled_timer_one
|
||||
ansible.builtin.systemd:
|
||||
name: "curl-localhost-test-one.timer"
|
||||
|
||||
- name: "Assert that the localhost-test-one timer is running"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- curl_scheduled_timer_one.status.ActiveState == "active"
|
||||
success_msg: "Timer is running"
|
||||
fail_msg: "Unexpected timer state: '{{ curl_scheduled_timer_one.status.ActiveState }}'"
|
||||
|
||||
- name: "Check the timer status - localhost-test-chained-curl"
|
||||
register: curl_scheduled_timer_chained_curl
|
||||
ansible.builtin.systemd:
|
||||
name: "curl-localhost-test-chained-curl.timer"
|
||||
|
||||
- name: "Assert that the localhost-test-chained-curl timer is running"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- curl_scheduled_timer_chained_curl.status.ActiveState == "active"
|
||||
success_msg: "Timer is running"
|
||||
fail_msg: "Unexpected timer state: '{{ curl_scheduled_timer_chained_curl.status.ActiveState }}'"
|
||||
|
||||
## Verify the systemd services
|
||||
|
||||
- name: "Check the service status - zero"
|
||||
register: curl_scheduled_service_zero
|
||||
ansible.builtin.systemd:
|
||||
name: "curl-localhost-test-zero.service"
|
||||
|
||||
- name: "Assert that the localhost-test-zero service exited with a 0/SUCCESS status"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- 'curl_scheduled_service_zero.status.ExecMainStatus == "0"'
|
||||
success_msg: "Service has exited with a 0/SUCCESS status"
|
||||
fail_msg: "Unexpected service status code: '{{ curl_scheduled_service_zero.status.ExecMainStatus }}'"
|
||||
|
||||
- name: "Check the service status - one"
|
||||
register: curl_scheduled_service_one
|
||||
ansible.builtin.systemd:
|
||||
name: "curl-localhost-test-one.service"
|
||||
|
||||
- name: "Assert that the localhost-test-one service exited with a 0/SUCCESS status"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- 'curl_scheduled_service_one.status.ExecMainStatus == "0"'
|
||||
success_msg: "Service has exited with a 0/SUCCESS status"
|
||||
fail_msg: "Unexpected service status code: '{{ curl_scheduled_service_one.status.ExecMainStatus }}'"
|
||||
|
||||
- name: "Check the service status - localhost-test-chained-curl"
|
||||
register: curl_scheduled_service_chained_curl
|
||||
ansible.builtin.systemd:
|
||||
name: "curl-localhost-test-chained-curl.service"
|
||||
|
||||
- name: "Assert that the localhost-test-chained-curl service exited with a 0/SUCCESS status"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- 'curl_scheduled_service_chained_curl.status.ExecMainStatus == "0"'
|
||||
success_msg: "Service has exited with a 0/SUCCESS status"
|
||||
fail_msg: "Unexpected service status code: '{{ curl_scheduled_service_chained_curl.status.ExecMainStatus }}'"
|
||||
33
roles/curl_scheduled/tasks/deploy-service.yml
Normal file
33
roles/curl_scheduled/tasks/deploy-service.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
- name: "Deploy-Service | Create systemd service file: '{{ job.label }}'"
|
||||
vars:
|
||||
label: "{{ job.label }}"
|
||||
url: "{{ job.url }}"
|
||||
curl_cmd: "{{ job.curl_cmd | default(curl_scheduled_curl_cmd) }}"
|
||||
curl_args: "{{ job.curl_args | default(curl_scheduled_curl_args) }}"
|
||||
register: curl_scheduled_service
|
||||
ansible.builtin.template:
|
||||
src: "placeholder.service"
|
||||
dest: "/etc/systemd/system/curl-{{ job.label }}.service"
|
||||
mode: "0660"
|
||||
validate: systemd-analyze verify %s
|
||||
|
||||
- name: "Deploy-Service | Create systemd timer file: '{{ job.label }}'"
|
||||
vars:
|
||||
label: "{{ job.label }}"
|
||||
schedule: "{{ job.schedule | default(curl_scheduled_schedule) }}"
|
||||
register: curl_scheduled_timer
|
||||
ansible.builtin.template:
|
||||
src: "placeholder.timer"
|
||||
dest: "/etc/systemd/system/curl-{{ job.label }}.timer"
|
||||
mode: "0660"
|
||||
validate: systemd-analyze verify %s
|
||||
|
||||
- name: "Deploy-Service | Enable and start the timer: '{{ job.label }}'" # noqa: no-handler
|
||||
become: true
|
||||
when: curl_scheduled_service.changed or curl_scheduled_timer.changed
|
||||
ansible.builtin.systemd:
|
||||
name: "curl-{{ job.label }}.timer"
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
19
roles/curl_scheduled/tasks/main.yml
Normal file
19
roles/curl_scheduled/tasks/main.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
- name: "Install curl (Debian derivatives)"
|
||||
when: ansible_os_family == "Debian"
|
||||
ansible.builtin.apt:
|
||||
name: curl
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: "Install curl (RHEL derivatives)"
|
||||
when: ansible_os_family == "RedHat"
|
||||
ansible.builtin.dnf:
|
||||
name: curl
|
||||
state: present
|
||||
|
||||
- name: "Configure and deploy systemd service"
|
||||
loop: "{{ services }}"
|
||||
loop_control:
|
||||
loop_var: "job"
|
||||
ansible.builtin.include_tasks: "deploy-service.yml"
|
||||
21
roles/curl_scheduled/templates/placeholder.service
Normal file
21
roles/curl_scheduled/templates/placeholder.service
Normal file
@@ -0,0 +1,21 @@
|
||||
[Unit]
|
||||
Description=Run an HTTP request via curl designated '{{ label }}'
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart={{ curl_cmd }} {{ curl_args }} "{{ url }}"
|
||||
Restart=no
|
||||
|
||||
# Security hardening
|
||||
ProtectSystem=strict
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectClock=yes
|
||||
RestrictSUIDSGID=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.targer
|
||||
12
roles/curl_scheduled/templates/placeholder.timer
Normal file
12
roles/curl_scheduled/templates/placeholder.timer
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Trigger a curl command designated '{{ label }}'
|
||||
Requires=curl-{{ label }}.service
|
||||
|
||||
[Timer]
|
||||
Unit=curl-{{ label }}.service
|
||||
OnCalendar={{ schedule }}
|
||||
AccuracySec=1m
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
2
roles/curl_scheduled/vars/main.yml
Normal file
2
roles/curl_scheduled/vars/main.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
curl_scheduled_kuma_port: "3001"
|
||||
37
roles/dnsmasq/README.md
Normal file
37
roles/dnsmasq/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
ansible-dnsmasq
|
||||
=========
|
||||
|
||||
Deploy dnsmasq on the target node. For now, supports only DNS functionality (DHCP and TFTP are not configuratble).
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Take care to open the port you choose for dnsmasq to serve queries on. This role does not handle firewall configuration.
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
None
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
```yaml
|
||||
roles:
|
||||
- role: genlab.dnsmasq
|
||||
```
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
corvus-migratorius@proton.me
|
||||
3
roles/dnsmasq/defaults/main.yml
Normal file
3
roles/dnsmasq/defaults/main.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
dnsmasq_cache_size: 100
|
||||
dnsmasq_dns_port: 5300
|
||||
7
roles/dnsmasq/handlers/main.yml
Normal file
7
roles/dnsmasq/handlers/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: "Restart dnsmasq"
|
||||
ansible.builtin.systemd_service:
|
||||
name: dnsmasq
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
enabled: true
|
||||
17
roles/dnsmasq/meta/main.yml
Normal file
17
roles/dnsmasq/meta/main.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: dnsmasq
|
||||
namespace: genlab
|
||||
author: "Alexander Gorelyshev"
|
||||
company: "Genlab, LLC"
|
||||
description: ""
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.1"
|
||||
|
||||
platforms:
|
||||
- name: "Ubuntu"
|
||||
versions: ["focal", "jammy"]
|
||||
|
||||
galaxy_tags: []
|
||||
|
||||
dependencies: []
|
||||
21
roles/dnsmasq/molecule/default/converge.yml
Normal file
21
roles/dnsmasq/molecule/default/converge.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
roles:
|
||||
- role: genlab.common.ufw
|
||||
disable_ipv6: true
|
||||
rules:
|
||||
- rule: allow
|
||||
# proto: udp
|
||||
port: 5300
|
||||
interface: lo
|
||||
direction: in
|
||||
comment: "Allow dnsmasq to serve DNS queries on the given interface"
|
||||
|
||||
- role: genlab.common.dnsmasq
|
||||
dnsmasq_iface: lo
|
||||
dnsmasq_domain: adm.local
|
||||
dnsmasq_dns_port: 5300
|
||||
dnsmasq_nodes:
|
||||
- name: hub
|
||||
ip: 127.0.0.1
|
||||
27
roles/dnsmasq/molecule/default/molecule.yml
Normal file
27
roles/dnsmasq/molecule/default/molecule.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: ubuntu
|
||||
image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest
|
||||
pre_build_image: true
|
||||
command: ${MOLECULE_DOCKER_COMMAND:-""}
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:rw
|
||||
cgroupns_mode: host
|
||||
privileged: true
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
lint: |
|
||||
set -e
|
||||
yamllint .
|
||||
ansible-lint .
|
||||
19
roles/dnsmasq/molecule/default/verify.yml
Normal file
19
roles/dnsmasq/molecule/default/verify.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
any_errors_fatal: true
|
||||
|
||||
pre_tasks:
|
||||
- name: "Install a package providing the `dig` tool"
|
||||
ansible.builtin.apt:
|
||||
name: dnsutils
|
||||
state: present
|
||||
|
||||
tasks:
|
||||
- name: "Test the output of the `dig` command"
|
||||
changed_when: false
|
||||
register: dnsmasq_dig
|
||||
failed_when: 'dnsmasq_dig.stdout != "127.0.0.1"'
|
||||
ansible.builtin.command:
|
||||
cmd: "dig @127.0.0.1 -p 5300 hub.adm.local +short"
|
||||
38
roles/dnsmasq/tasks/main.yml
Normal file
38
roles/dnsmasq/tasks/main.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
- name: "Install dnsmasq"
|
||||
ansible.builtin.apt:
|
||||
name: dnsmasq
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
|
||||
- name: "Create interface-specific configuration file"
|
||||
notify: "Restart dnsmasq"
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/dnsmasq.d/{{ dnsmasq_domain }}.conf
|
||||
create: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0660"
|
||||
block: |
|
||||
interface="{{ dnsmasq_iface }}"
|
||||
port="{{ dnsmasq_dns_port }}"
|
||||
cache-size="{{ dnsmasq_cache_size }}"
|
||||
log-queries
|
||||
server=1.1.1.1 # Cloudflare DNS
|
||||
server=8.8.8.8 # Google DNS
|
||||
server=8.8.4.4 # Google DNS (secondary)
|
||||
|
||||
|
||||
- name: "Add dnsmasq_nodes to the configuration file"
|
||||
notify: "Restart dnsmasq"
|
||||
loop: "{{ dnsmasq_nodes }}"
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/dnsmasq.d/{{ dnsmasq_domain }}.conf
|
||||
marker: "# {mark} ANSIBLE MANAGED: {{ item.name }}.{{ dnsmasq_domain }}"
|
||||
block: |
|
||||
address=/{{ item.name }}.{{ dnsmasq_domain }}/{{ item.ip }}
|
||||
|
||||
|
||||
- name: "Flush handlers"
|
||||
ansible.builtin.meta: flush_handlers
|
||||
1
roles/dnsmasq/vars/main.yml
Normal file
1
roles/dnsmasq/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
107
roles/grafana/README.md
Normal file
107
roles/grafana/README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
genlab.grafana
|
||||
=========
|
||||
|
||||
This ansible role installs [Grafana](https://github.com/grafana/grafana) - the open-source platform for monitoring and observability. It can produce charts, graphs, and alerts for the web when connected to supported data sources.
|
||||
|
||||
This role installs and configures Grafana from a binary distribution. It also:
|
||||
|
||||
- Uploads custom dashboards
|
||||
|
||||
- Installs plugins
|
||||
|
||||
- Imports public dashboards and data sources
|
||||
|
||||
- Allows admin password changes
|
||||
|
||||
Supports user creation
|
||||
Grafana service requires an environment file at startup, where you can set custom paths for logs (`grafana_log_dir`), data (`grafana_data_dir`), and plugins (`grafana_plugins_dir`). This allows flexible control over Grafana's data storage.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
You need `community.grafana` module.
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
```
|
||||
grafana_user: "grafana" # user name
|
||||
grafana_group: "grafana" # group name
|
||||
grafana_version: 11.5.0 # version
|
||||
grafana_port: 3000 # port
|
||||
|
||||
# Directory paths
|
||||
grafana_dashboard_dir: "/tmp/grafana/dashboards" # where to copy dashboards from source
|
||||
grafana_plugins_dir: "/var/lib/grafana/plugins" # where to store plugins on target
|
||||
grafana_datasource_dir: "/etc/grafana/provisioning/datasources" # where to store data sources on target
|
||||
grafana_log_dir: "/var/log/grafana" # where to write logs
|
||||
grafana_data_dir: "/var/lib/grafana" # where to store Grafana DB
|
||||
|
||||
# Optional configurations
|
||||
grafana_users: [] # array of user names, passwords, and statuses
|
||||
grafana_plugins: [] # array of plugins to install
|
||||
grafana_public_dashboards: [] # array of public dashboards to import
|
||||
|
||||
# Admin credentials
|
||||
admin_api_username: "secret" # Grafana admin username
|
||||
admin_api_password: "secret" # Grafana admin password
|
||||
|
||||
# Source paths
|
||||
dashboard_source_path: "mydir/dashboards" # path to dashboards on source server
|
||||
datasource_source_path: "mydir/datasources" # path to data sources on source server
|
||||
```
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
vars:
|
||||
grafana_users:
|
||||
- name: "test"
|
||||
user_login: "test"
|
||||
user_password: "test"
|
||||
user_email: "test@mail.ru"
|
||||
is_admin: false
|
||||
grafana_plugins:
|
||||
version: 2.1.8
|
||||
- name: aceiot-svg-panel
|
||||
version: 0.1.5
|
||||
grafana_public_dashboards:
|
||||
- name: Node Full Exporter
|
||||
id: 1860
|
||||
revision: 36
|
||||
vars_files:
|
||||
- secrets/admin_cred.yml
|
||||
|
||||
roles:
|
||||
- role: genlab.grafana
|
||||
grafana_version: 11.5.0
|
||||
admin_api_username: "{{ grafana.admin_api_username }}"
|
||||
admin_api_password: "{{ grafana.admin_api_password }}"
|
||||
users: "{{ grafana_users }}"
|
||||
plugins: "{{ grafana_plugins }}"
|
||||
public_dashboards: "{{ grafana_public_dashboards }}"
|
||||
dashboard_source_path: "molecule/default/dashboards"
|
||||
datasource_source_path: "molecule/default/datasources"
|
||||
grafana_log_dir: "/opt/grafana/data"
|
||||
grafana_data_dir: "/opt/grafana/lib"
|
||||
|
||||
```
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
corvus-migratorius@proton.me
|
||||
10
roles/grafana/defaults/main.yml
Normal file
10
roles/grafana/defaults/main.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
grafana_user: "grafana"
|
||||
grafana_group: "grafana"
|
||||
grafana_version: 11.5.0
|
||||
grafana_port: 3000
|
||||
grafana_dashboard_dir: "/tmp/grafana/dashboards"
|
||||
grafana_plugins_dir: "/var/lib/grafana/plugins"
|
||||
grafana_datasource_dir: "/etc/grafana/provisioning/datasources"
|
||||
grafana_log_dir: "/var/log/grafana"
|
||||
grafana_data_dir: "/var/lib/grafana"
|
||||
7
roles/grafana/handlers/main.yml
Normal file
7
roles/grafana/handlers/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: "(Re)start and enable Grafana"
|
||||
ansible.builtin.systemd_service:
|
||||
name: grafana-server
|
||||
state: restarted
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
17
roles/grafana/meta/main.yml
Normal file
17
roles/grafana/meta/main.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: "grafana"
|
||||
namespace: genlab
|
||||
author: "Alexander Gorelyshev"
|
||||
company: "Genlab, LLC"
|
||||
description: ""
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.1"
|
||||
|
||||
platforms:
|
||||
- name: "Ubuntu"
|
||||
versions: ["focal", "jammy", "noble"]
|
||||
|
||||
galaxy_tags: []
|
||||
|
||||
dependencies: []
|
||||
44
roles/grafana/molecule/default/converge.yml
Normal file
44
roles/grafana/molecule/default/converge.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
vars:
|
||||
grafana_users:
|
||||
- name: "test"
|
||||
user_login: "test"
|
||||
# kics-scan ignore-line
|
||||
user_password: "test"
|
||||
user_email: "test@mail.ru"
|
||||
is_admin: false
|
||||
- name: "test2"
|
||||
user_login: "test2"
|
||||
# kics-scan ignore-line
|
||||
user_password: "test2"
|
||||
user_email: "test2@mail.ru"
|
||||
is_admin: true
|
||||
grafana_plugins:
|
||||
- name: grafana-metricsdrilldown-app
|
||||
version: 1.0.0
|
||||
- name: grafana-clock-panel
|
||||
version: 2.1.8
|
||||
- name: aceiot-svg-panel
|
||||
version: 0.1.5
|
||||
grafana_public_dashboards:
|
||||
- name: Node Full Exporter
|
||||
id: 1860
|
||||
revision: 36
|
||||
vars_files:
|
||||
- secrets/admin_cred.yml
|
||||
|
||||
roles:
|
||||
# kics-scan ignore-block
|
||||
- role: genlab.common.grafana
|
||||
grafana_version: 11.5.0
|
||||
admin_api_username: "{{ grafana.admin_api_username }}"
|
||||
admin_api_password: "{{ grafana.admin_api_password }}"
|
||||
users: "{{ grafana_users }}"
|
||||
plugins: "{{ grafana_plugins }}"
|
||||
public_dashboards: "{{ grafana_public_dashboards }}"
|
||||
dashboard_source_path: "molecule/default/dashboards"
|
||||
datasource_source_path: "molecule/default/datasources"
|
||||
grafana_log_dir: "/opt/grafana/data"
|
||||
grafana_data_dir: "/opt/grafana/lib"
|
||||
700
roles/grafana/molecule/default/dashboards/test_dashboard.json
Normal file
700
roles/grafana/molecule/default/dashboards/test_dashboard.json
Normal file
@@ -0,0 +1,700 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_PROMETHEUS",
|
||||
"label": "Prometheus",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "prometheus",
|
||||
"pluginName": "Prometheus"
|
||||
}
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "bargauge",
|
||||
"name": "Bar gauge",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "7.1.5"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "graph",
|
||||
"name": "Graph",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
"id": "prometheus",
|
||||
"name": "Prometheus",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "stat",
|
||||
"name": "Stat",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "table",
|
||||
"name": "Table",
|
||||
"version": ""
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "grafana"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": 29,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"decimals": 0,
|
||||
"mappings": [],
|
||||
"min": 0,
|
||||
"noValue": "0",
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "#EAB839",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "none"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 15,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"displayMode": "gradient",
|
||||
"minVizHeight": 10,
|
||||
"minVizWidth": 0,
|
||||
"orientation": "horizontal",
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"last"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showUnfilled": true,
|
||||
"valueMode": "color"
|
||||
},
|
||||
"pluginVersion": "10.1.5",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum(policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", namespace=~\"$namespace\", status=~\"fail|error\" } > 0) by (namespace)",
|
||||
"instant": true,
|
||||
"interval": "",
|
||||
"legendFormat": "{{namespace}}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Failing Policies by Namespace",
|
||||
"type": "bargauge"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"decimals": 0,
|
||||
"mappings": [],
|
||||
"min": 0,
|
||||
"noValue": "0",
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "#EAB839",
|
||||
"value": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 9,
|
||||
"x": 15,
|
||||
"y": 0
|
||||
},
|
||||
"id": 5,
|
||||
"options": {
|
||||
"colorMode": "value",
|
||||
"graphMode": "area",
|
||||
"justifyMode": "center",
|
||||
"orientation": "vertical",
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"last"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "10.1.5",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum(cluster_policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", status=~\"fail|error\" } > 0) by (status)",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "{{ status }}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Failing ClusterPolicies",
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 11,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
"current": true,
|
||||
"hideEmpty": true,
|
||||
"hideZero": true,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null as zero",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "10.1.5",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum(cluster_policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", status=~\"fail|error\" } > 0) by (policy)",
|
||||
"interval": "",
|
||||
"legendFormat": "{{ policy }}",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum(policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", namespace=~\"$namespace\", status=~\"fail|error\" } > 0) by (policy)",
|
||||
"interval": "",
|
||||
"legendFormat": "{{ policy }}",
|
||||
"refId": "B"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeRegions": [],
|
||||
"title": "Failing Policies Graph",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"mode": "time",
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"decimals": 0,
|
||||
"format": "short",
|
||||
"logBase": 1,
|
||||
"min": "0",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"logBase": 1,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"cellOptions": {
|
||||
"type": "auto"
|
||||
},
|
||||
"inspect": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 18
|
||||
},
|
||||
"id": 7,
|
||||
"options": {
|
||||
"cellHeight": "sm",
|
||||
"footer": {
|
||||
"countRows": false,
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "10.1.5",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum(policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", namespace=~\"$namespace\", status=~\"fail|error\" }) by (namespace,policy,rule,kind,name,status,category,severity,source)",
|
||||
"format": "table",
|
||||
"instant": true,
|
||||
"interval": "",
|
||||
"legendFormat": "{{namespace}}: {{ policy }}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Failing PolicyRules",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"excludeByName": {
|
||||
"Time": true,
|
||||
"Value": true
|
||||
},
|
||||
"indexByName": {
|
||||
"category": 1,
|
||||
"kind": 4,
|
||||
"name": 5,
|
||||
"namespace": 3,
|
||||
"policy": 6,
|
||||
"rule": 7,
|
||||
"severity": 2,
|
||||
"source": 0,
|
||||
"status": 8
|
||||
},
|
||||
"renameByName": {
|
||||
"namespace": "namespace"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {
|
||||
"cellOptions": {
|
||||
"type": "auto"
|
||||
},
|
||||
"inspect": false
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 28
|
||||
},
|
||||
"id": 9,
|
||||
"options": {
|
||||
"cellHeight": "sm",
|
||||
"footer": {
|
||||
"countRows": false,
|
||||
"fields": "",
|
||||
"reducer": [
|
||||
"sum"
|
||||
],
|
||||
"show": false
|
||||
},
|
||||
"showHeader": true
|
||||
},
|
||||
"pluginVersion": "10.1.5",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum(cluster_policy_report_result{policy=~\"$policy\", category=~\"$category\", severity=~\"$severity\", source=~\"$source\", kind=~\"$kind\", status=~\"fail|error\" }) by (policy,rule,kind,name,status,category,severity,source)",
|
||||
"format": "table",
|
||||
"instant": true,
|
||||
"interval": "",
|
||||
"legendFormat": "{{ kind }}: {{ name }} - {{ policy }}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Failing ClusterPolicyRules",
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"excludeByName": {
|
||||
"Time": true,
|
||||
"Value": true,
|
||||
"__name__": true,
|
||||
"container": true,
|
||||
"endpoint": true,
|
||||
"instance": true,
|
||||
"job": true,
|
||||
"namespace": true,
|
||||
"pod": true,
|
||||
"report": true,
|
||||
"service": true
|
||||
},
|
||||
"indexByName": {
|
||||
"category": 1,
|
||||
"kind": 3,
|
||||
"name": 4,
|
||||
"policy": 5,
|
||||
"rule": 6,
|
||||
"severity": 2,
|
||||
"source": 0,
|
||||
"status": 7
|
||||
},
|
||||
"renameByName": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "table"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 38,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"Policy Reporter"
|
||||
],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "Prometheus",
|
||||
"value": "prometheus"
|
||||
},
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": "Datasource",
|
||||
"multi": false,
|
||||
"name": "DS_PROMETHEUS",
|
||||
"options": [],
|
||||
"query": "prometheus",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"allValue": ".*",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "All",
|
||||
"value": "$__all"
|
||||
},
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"definition": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, policy)",
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "Policy",
|
||||
"multi": true,
|
||||
"name": "policy",
|
||||
"options": [],
|
||||
"query": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, policy)",
|
||||
"refresh": 2,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 5,
|
||||
"tagValuesQuery": "",
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
},
|
||||
{
|
||||
"allValue": ".*",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "All",
|
||||
"value": "$__all"
|
||||
},
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"definition": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, category)",
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "Category",
|
||||
"multi": true,
|
||||
"name": "category",
|
||||
"options": [],
|
||||
"query": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, category)",
|
||||
"refresh": 2,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 5,
|
||||
"tagValuesQuery": "",
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
},
|
||||
{
|
||||
"allValue": ".*",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "All",
|
||||
"value": "$__all"
|
||||
},
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"definition": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, severity)",
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "Severity",
|
||||
"multi": true,
|
||||
"name": "severity",
|
||||
"options": [],
|
||||
"query": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, severity)",
|
||||
"refresh": 2,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 5,
|
||||
"tagValuesQuery": "",
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
},
|
||||
{
|
||||
"allValue": ".*",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "All",
|
||||
"value": "$__all"
|
||||
},
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"definition": "label_values({__name__= \"policy_report_result\", status=~\"fail|error\"}, namespace)",
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "Namespace",
|
||||
"multi": true,
|
||||
"name": "namespace",
|
||||
"options": [],
|
||||
"query": "label_values({__name__= \"policy_report_result\", status=~\"fail|error\"}, namespace)",
|
||||
"refresh": 2,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 5,
|
||||
"tagValuesQuery": "",
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
},
|
||||
{
|
||||
"allValue": ".*",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "All",
|
||||
"value": "$__all"
|
||||
},
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"definition": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, kind)",
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "Kind",
|
||||
"multi": true,
|
||||
"name": "kind",
|
||||
"options": [],
|
||||
"query": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, kind)",
|
||||
"refresh": 2,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 5,
|
||||
"tagValuesQuery": "",
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
},
|
||||
{
|
||||
"allValue": ".*",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "All",
|
||||
"value": "$__all"
|
||||
},
|
||||
"datasource": {
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"definition": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, source)",
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "Source",
|
||||
"multi": true,
|
||||
"name": "source",
|
||||
"options": [],
|
||||
"query": "label_values({__name__=~ \"policy_report_result|cluster_policy_report_result\", status=~\"fail|error\"}, source)",
|
||||
"refresh": 2,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 5,
|
||||
"tagValuesQuery": "",
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-30m",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "PolicyReports",
|
||||
"uid": "ZkwXrUMnk",
|
||||
"version": 1,
|
||||
"gnetId": 13968
|
||||
}
|
||||
2081
roles/grafana/molecule/default/dashboards/test_dashboard2.json
Normal file
2081
roles/grafana/molecule/default/dashboards/test_dashboard2.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Loki
|
||||
type: loki
|
||||
access: proxy
|
||||
basicAuth: false
|
||||
basicAuthPassword: false
|
||||
url: http://localhost:3100
|
||||
jsonData:
|
||||
maxLines: 1000
|
||||
httpHeaderName1: "Authorization"
|
||||
secureJsonData:
|
||||
httpHeaderValue1: "Bearer your_token_here"
|
||||
version: 1
|
||||
editable: false
|
||||
@@ -0,0 +1,50 @@
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# list of datasources that should be deleted from the database
|
||||
# deleteDatasources:
|
||||
# - name: Prometheus
|
||||
# orgId: 1
|
||||
|
||||
# list of datasources to insert/update depending
|
||||
# whats available in the database
|
||||
datasources:
|
||||
# <string, required> name of the datasource. Required
|
||||
- name: Prometheus
|
||||
# <string, required> datasource type. Required
|
||||
type: prometheus
|
||||
# <string, required> access mode. direct or proxy. Required
|
||||
access: proxy
|
||||
# <int> org id. will default to orgId 1 if not specified
|
||||
orgId: 1
|
||||
# <string> url
|
||||
url: http://localhost:9090
|
||||
# <string> database password, if used
|
||||
password:
|
||||
# <string> database user, if used
|
||||
user:
|
||||
# <string> database name, if used
|
||||
database:
|
||||
# <bool> enable/disable basic auth
|
||||
basicAuth: false
|
||||
# <string> basic auth username, if used
|
||||
basicAuthUser:
|
||||
# <string> basic auth password, if used
|
||||
basicAuthPassword:
|
||||
# <bool> enable/disable with credentials headers
|
||||
withCredentials:
|
||||
# <bool> mark as default datasource. Max one per org
|
||||
isDefault: true
|
||||
# <map> fields that will be converted to json and stored in json_data
|
||||
jsonData:
|
||||
graphiteVersion: "1.1"
|
||||
tlsAuth: false
|
||||
tlsAuthWithCACert: false
|
||||
# <string> json object of data that will be encrypted.
|
||||
secureJsonData:
|
||||
tlsCACert: "..."
|
||||
tlsClientCert: "..."
|
||||
tlsClientKey: "..."
|
||||
version: 1
|
||||
# <bool> allow users to edit datasources from the UI.
|
||||
editable: true
|
||||
29
roles/grafana/molecule/default/molecule.yml
Normal file
29
roles/grafana/molecule/default/molecule.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: ubuntu
|
||||
image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest
|
||||
pre_build_image: true
|
||||
command: ${MOLECULE_DOCKER_COMMAND:-""}
|
||||
published_ports:
|
||||
- 127.0.0.1:3001:3000
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:rw
|
||||
cgroupns_mode: host
|
||||
privileged: true
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
lint: |
|
||||
set -e
|
||||
yamllint .
|
||||
ansible-lint .
|
||||
3
roles/grafana/molecule/default/secrets/admin_cred.yml
Normal file
3
roles/grafana/molecule/default/secrets/admin_cred.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
grafana:
|
||||
admin_api_username: "admin"
|
||||
admin_api_password: "changme23"
|
||||
41
roles/grafana/molecule/default/verify.yml
Normal file
41
roles/grafana/molecule/default/verify.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
any_errors_fatal: true
|
||||
vars_files:
|
||||
- admin_cred.yml
|
||||
|
||||
tasks:
|
||||
|
||||
- name: "Include default vars"
|
||||
ansible.builtin.include_vars:
|
||||
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/"
|
||||
extensions: ['yml']
|
||||
|
||||
- name: "Check if Grafana is installed"
|
||||
changed_when: false
|
||||
ansible.builtin.command: "grafana-server -v"
|
||||
register: grafana_installed_version
|
||||
|
||||
- name: "Check Grafana version"
|
||||
ansible.builtin.assert:
|
||||
that: "grafana_installed_version.stdout is regex('{{ grafana_version }}')"
|
||||
success_msg: "grafana version {{ grafana_version }} is installed and working"
|
||||
fail_msg: "grafana version {{ grafana_version }} is not installed or not working correctly"
|
||||
|
||||
# kics-scan ignore-block
|
||||
- name: "Check if Grafana login page is reachable"
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:{{ grafana_port }}/api/health"
|
||||
return_content: true
|
||||
status_code: 200
|
||||
method: GET
|
||||
body_format: json
|
||||
register: grafana_health
|
||||
|
||||
- name: "Debug Grafana health status"
|
||||
ansible.builtin.assert:
|
||||
that: grafana_health.json.database == "ok"
|
||||
success_msg: "Grafana is healthy"
|
||||
fail_msg: "Grafana is not healthy"
|
||||
40
roles/grafana/tasks/dashboards.yml
Normal file
40
roles/grafana/tasks/dashboards.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
- name: "Create dashboard directory"
|
||||
ansible.builtin.file:
|
||||
path: "{{ grafana_dashboard_dir }}"
|
||||
state: directory
|
||||
owner: "{{ grafana_user }}"
|
||||
group: "{{ grafana_group }}"
|
||||
mode: "0755"
|
||||
|
||||
- name: "Copy dashboard files from source to target"
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ grafana_dashboard_dir }}/{{ item | basename }}"
|
||||
mode: "0644"
|
||||
owner: "{{ grafana_user }}"
|
||||
group: "{{ grafana_group }}"
|
||||
with_fileglob:
|
||||
- "{{ dashboard_source_path }}/*.json"
|
||||
|
||||
- name: "Import Grafana dashboards to Grafana"
|
||||
community.grafana.grafana_dashboard:
|
||||
grafana_url: "http://127.0.0.1:{{ grafana_port }}"
|
||||
url_username: "{{ admin_api_username }}"
|
||||
url_password: "{{ admin_api_password }}"
|
||||
state: present
|
||||
commit_message: Updated by ansible
|
||||
overwrite: false
|
||||
path: "{{ grafana_dashboard_dir }}/{{ item | basename }}"
|
||||
with_fileglob:
|
||||
- "{{ dashboard_source_path }}/*.json"
|
||||
|
||||
- name: "Run | reload Grafana provisioned dashboard configurations"
|
||||
ansible.builtin.uri:
|
||||
# kics-scan ignore-line
|
||||
url: "http://127.0.0.1:{{ grafana_port }}/api/admin/provisioning/dashboards/reload"
|
||||
method: POST
|
||||
force_basic_auth: true
|
||||
user: "{{ admin_api_username }}"
|
||||
password: "{{ admin_api_password }}"
|
||||
status_code: 200
|
||||
28
roles/grafana/tasks/datasources.yml
Normal file
28
roles/grafana/tasks/datasources.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
- name: "Create datasource directory"
|
||||
ansible.builtin.file:
|
||||
path: "{{ grafana_datasource_dir }}"
|
||||
state: directory
|
||||
owner: "{{ grafana_user }}"
|
||||
group: "{{ grafana_group }}"
|
||||
mode: "0755"
|
||||
|
||||
- name: "Configure | provision datasources for Grafana"
|
||||
ansible.builtin.copy:
|
||||
src: "{{ datasource_source_path }}/{{ item | basename }}"
|
||||
dest: "{{ grafana_datasource_dir }}/{{ item | basename }}"
|
||||
owner: "{{ grafana_user }}"
|
||||
group: "{{ grafana_group }}"
|
||||
mode: "0660"
|
||||
with_fileglob:
|
||||
"{{ datasource_source_path }}/*.y*ml"
|
||||
|
||||
- name: "Run | reload Grafana datasource provisioned configurations"
|
||||
ansible.builtin.uri:
|
||||
# kics-scan ignore-line
|
||||
url: "http://127.0.0.1:{{ grafana_port }}/api/admin/provisioning/datasources/reload"
|
||||
method: POST
|
||||
force_basic_auth: true
|
||||
user: "{{ admin_api_username }}"
|
||||
password: "{{ admin_api_password }}"
|
||||
status_code: 200
|
||||
11
roles/grafana/tasks/import_pub_dashboard.yml
Normal file
11
roles/grafana/tasks/import_pub_dashboard.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: "Import public dashboard - '{{ public_dashboard.name }}''"
|
||||
community.grafana.grafana_dashboard:
|
||||
grafana_url: "http://127.0.0.1:{{ grafana_port }}"
|
||||
state: "{{ public_dashboard.state | default('present') }}"
|
||||
overwrite: false
|
||||
dashboard_id: "{{ public_dashboard.id }}"
|
||||
dashboard_revision: "{{ public_dashboard.revision }}"
|
||||
commit_message: "Add public dashboard '{{ public_dashboard.name }}''"
|
||||
url_username: "{{ admin_api_username }}"
|
||||
url_password: "{{ admin_api_password }}"
|
||||
78
roles/grafana/tasks/install.yml
Normal file
78
roles/grafana/tasks/install.yml
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
- name: "Create Grafana system group"
|
||||
ansible.builtin.group:
|
||||
name: grafana
|
||||
system: true
|
||||
state: present
|
||||
|
||||
- name: "Create Grafana system user"
|
||||
ansible.builtin.user:
|
||||
name: grafana
|
||||
group: grafana
|
||||
system: true
|
||||
shell: "/sbin/nologin"
|
||||
create_home: false
|
||||
state: present
|
||||
|
||||
- name: "Install Grafana deb package"
|
||||
block:
|
||||
- name: "Check Grafana version"
|
||||
changed_when: false
|
||||
ansible.builtin.command:
|
||||
cmd: "grafana-server --version"
|
||||
register: grafana_ver
|
||||
|
||||
- name: "Assert version correctness"
|
||||
ansible.builtin.assert:
|
||||
that: "grafana_ver.stdout is regex('{{ grafana_version }}')"
|
||||
success_msg: "grafana version {{ grafana_version }} is installed and working"
|
||||
fail_msg: "grafana version {{ grafana_version }} is not installed or not working correctly"
|
||||
|
||||
rescue:
|
||||
- name: "Ensure that directories exist"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ grafana_user }}"
|
||||
group: "{{ grafana_group }}"
|
||||
mode: '0775'
|
||||
with_items:
|
||||
- "{{ grafana_log_dir }}"
|
||||
- "{{ grafana_data_dir }}"
|
||||
- "{{ grafana_plugins_dir }}"
|
||||
|
||||
- name: "Download Grafana DEB package"
|
||||
ansible.builtin.get_url:
|
||||
url: "https://dl.grafana.com/oss/release/grafana_{{ grafana_version }}_amd64.deb"
|
||||
dest: "/tmp/grafana-{{ grafana_version }}_amd64.deb"
|
||||
owner: "{{ grafana_user }}"
|
||||
group: "{{ grafana_group }}"
|
||||
mode: "0644"
|
||||
register: grafana_package_tmp
|
||||
|
||||
- name: "Install DEB package"
|
||||
notify: "(Re)start and enable Grafana"
|
||||
ansible.builtin.apt:
|
||||
deb: "{{ grafana_package_tmp.dest }}"
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: "Cleanup downloaded file"
|
||||
ansible.builtin.file:
|
||||
path: "/tmp/grafana-{{ grafana_version }}_amd64.deb"
|
||||
state: absent
|
||||
|
||||
- name: "Create env file for systemd service unit"
|
||||
notify: "(Re)start and enable Grafana"
|
||||
ansible.builtin.template:
|
||||
src: grafana-server.env.j2
|
||||
dest: "{{ item }}"
|
||||
owner: "{{ grafana_user }}"
|
||||
group: "{{ grafana_group }}"
|
||||
mode: "0660"
|
||||
with_items:
|
||||
- "/etc/default/grafana-server"
|
||||
- "/etc/default/grafana"
|
||||
|
||||
- name: "Flush handlers"
|
||||
ansible.builtin.meta: "flush_handlers"
|
||||
39
roles/grafana/tasks/main.yml
Normal file
39
roles/grafana/tasks/main.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
- name: "Include grafana installation tasks"
|
||||
ansible.builtin.include_tasks: install.yml
|
||||
|
||||
- name: "Wait for the Grafana server to become available"
|
||||
ansible.builtin.wait_for:
|
||||
host: "127.0.0.1"
|
||||
port: "{{ grafana_port }}"
|
||||
state: started
|
||||
delay: 10
|
||||
|
||||
- name: "Include user creation tasks"
|
||||
when: users is defined
|
||||
loop: "{{ users }}"
|
||||
loop_control:
|
||||
loop_var: user
|
||||
ansible.builtin.include_tasks: user.yml
|
||||
|
||||
- name: "Configure custom dashboards"
|
||||
when: dashboard_source_path is defined
|
||||
ansible.builtin.include_tasks: dashboards.yml
|
||||
|
||||
- name: "Configure public dashboards"
|
||||
when: public_dashboards is defined
|
||||
loop: "{{ public_dashboards }}"
|
||||
loop_control:
|
||||
loop_var: public_dashboard
|
||||
ansible.builtin.include_tasks: import_pub_dashboard.yml
|
||||
|
||||
- name: "Configure plugins"
|
||||
when: plugins is defined
|
||||
loop: "{{ plugins }}"
|
||||
loop_control:
|
||||
loop_var: plugin
|
||||
ansible.builtin.include_tasks: plugins.yml
|
||||
|
||||
- name: "Configure datasources"
|
||||
when: datasource_source_path is defined
|
||||
ansible.builtin.include_tasks: datasources.yml
|
||||
16
roles/grafana/tasks/plugins.yml
Normal file
16
roles/grafana/tasks/plugins.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
- name: "Create plugin directory"
|
||||
ansible.builtin.file:
|
||||
path: "{{ grafana_plugins_dir }}"
|
||||
state: directory
|
||||
owner: "{{ grafana_user }}"
|
||||
group: "{{ grafana_group }}"
|
||||
mode: "0755"
|
||||
|
||||
- name: "Install Grafana plugins"
|
||||
community.grafana.grafana_plugin:
|
||||
name: "{{ plugin.name }}"
|
||||
version: "{{ plugin.version }}"
|
||||
grafana_plugins_dir: "{{ grafana_plugins_dir }}"
|
||||
state: "{{ plugin.state | default('present') }}"
|
||||
notify: "(Re)start and enable Grafana"
|
||||
19
roles/grafana/tasks/user.yml
Normal file
19
roles/grafana/tasks/user.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
|
||||
- name: "Reset default admin password"
|
||||
ansible.builtin.command: >
|
||||
grafana-cli admin reset-admin-password "{{ admin_api_password }}"
|
||||
no_log: true
|
||||
changed_when: false
|
||||
|
||||
- name: "Create | update a Grafana user"
|
||||
community.grafana.grafana_user:
|
||||
url: "http://127.0.0.1:{{ grafana_port }}"
|
||||
url_username: "{{ admin_api_username }}"
|
||||
url_password: "{{ admin_api_password }}"
|
||||
name: "{{ user.name }}"
|
||||
email: "{{ user.user_email }}"
|
||||
login: "{{ user.user_login }}"
|
||||
password: "{{ user.user_password }}"
|
||||
is_admin: "{{ user.is_admin | default(false) }}"
|
||||
state: present
|
||||
24
roles/grafana/templates/grafana-server.env.j2
Normal file
24
roles/grafana/templates/grafana-server.env.j2
Normal file
@@ -0,0 +1,24 @@
|
||||
GRAFANA_USER=grafana
|
||||
|
||||
GRAFANA_GROUP=grafana
|
||||
|
||||
GRAFANA_HOME=/usr/share/grafana
|
||||
|
||||
LOG_DIR={{ grafana_log_dir }}
|
||||
|
||||
DATA_DIR={{ grafana_data_dir }}
|
||||
|
||||
MAX_OPEN_FILES=10000
|
||||
|
||||
CONF_DIR=/etc/grafana
|
||||
|
||||
CONF_FILE=/etc/grafana/grafana.ini
|
||||
|
||||
RESTART_ON_UPGRADE=true
|
||||
|
||||
PLUGINS_DIR={{ grafana_plugins_dir }}
|
||||
|
||||
PROVISIONING_CFG_DIR=/etc/grafana/provisioning
|
||||
|
||||
# Only used on systemd systems
|
||||
PID_FILE_DIR=/run/grafana
|
||||
1
roles/grafana/vars/main.yml
Normal file
1
roles/grafana/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
61
roles/ipmi_exporter/README.md
Normal file
61
roles/ipmi_exporter/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
genlab.ipmi_exporter
|
||||
=========
|
||||
|
||||
This Ansible role installs ipmi_exporter on target host. This is a Prometheus exporter for Intelligent Platform Management Interface [metrics](https://github.com/prometheus-community/ipmi_exporter/blob/master/docs/metrics.md)
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
By default, the exporter relies on tools from the FreeIPMI suite for the actual IPMI implementation.
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
Configuration files must have names ```web_conf.yaml``` and ```ipmi_local.conf```. If ipmi_exp_source_dir is specified, the role searches for ```web_conf.yaml``` and ```ipmi_local.conf``` in that directory and copy to target host in ```ipmi_exp_config_dir```. If the source directory is not specified, the role skips this step. In ipmi_local.conf user can describe what modules to use for metric collection.
|
||||
|
||||
ipmi_up{collector="<NAME>"} is 1 if the data for this collector could successfully be retrieved from the remote host, 0 otherwise. The following collectors are available and can be enabled or disabled in the config:
|
||||
- ipmi: collects IPMI sensor data. If it fails, sensor metrics (see below) will not be available
|
||||
- dcmi: collects DCMI data, currently only power consumption. If it fails, power consumption metrics (see below) will not be available
|
||||
- bmc: collects BMC details. If it fails, BMC info metrics (see below) will not be available
|
||||
- bmc-watchdog: collects status of the watchdog. If it fails, BMC watchdog metrics (see below) will not be available
|
||||
- chassis: collects the current chassis power state (on/off). If it fails, the chassis power state metric (see below) will not be available
|
||||
- sel: collects system event log (SEL) details. If it fails, SEL metrics (see below) will not be available
|
||||
- sel-events: collects metrics for user-defined events in system event log (SEL). If it fails, SEL entries metrics (see below) will not be available
|
||||
- sm-lan-mode: collects the "LAN mode" setting in the current BMC config. If it fails, the LAN mode metric (see below) will not be available
|
||||
|
||||
```yaml
|
||||
ipmi_exp_version: 1.10.1
|
||||
ipmi_exp_dir: "/etc/exporters"
|
||||
ipmi_exp_config_dir: "/etc/exporters/config"
|
||||
ipmi_exp_args: "" # --[no-]native-ipmi Use native IPMI implementation instead of FreeIPMI (EXPERIMENTAL)
|
||||
# --[no-]web.systemd-socket Use systemd socket activation listeners instead of port listeners (Linux only).
|
||||
ipmi_exp_log_level: "info" # Only log messages with the given severity or above. One of: [debug, info, warn, error]
|
||||
ipmi_exp_log_format: "logfmt" # Output format of log messages. One of: [logfmt, json]
|
||||
ipmi_exp_web_listen_address: "localhost:9290" # Addresses on which to expose metrics and web interface. Repeatable for multiple addresses. Examples: `:9100` or `[::1]:9100` for http, vsock://:9100` for vsock
|
||||
ipmi_exp_source_dir: ipmi_local.conf # Path to configuration file. See: https://github.com/prometheus-community/ipmi_exporter/blob/master/docs/configuration.md
|
||||
ipmi_exp_web_source_dir: web_conf.yaml # Path to configuration file that can enable TLS or authentication. See: https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md
|
||||
```
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
```yaml
|
||||
roles:
|
||||
- role: genlab.ipmi_exporter
|
||||
ipmi_exp_version: "1.10.1"
|
||||
ipmi_exp_source_dir: "molecule/default/"
|
||||
```
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
corvus-migratorius@proton.me
|
||||
8
roles/ipmi_exporter/defaults/main.yml
Normal file
8
roles/ipmi_exporter/defaults/main.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
ipmi_exporter_version: 1.10.1
|
||||
ipmi_exporter_dir: "/etc/exporters"
|
||||
ipmi_exporter_config_dir: "/etc/exporters/config"
|
||||
ipmi_exporter_log_level: "info"
|
||||
ipmi_exporter_log_format: "logfmt"
|
||||
ipmi_exporter_web_listen_address: "localhost:9290"
|
||||
ipmi_exporter_args: ""
|
||||
7
roles/ipmi_exporter/handlers/main.yml
Normal file
7
roles/ipmi_exporter/handlers/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: "(Re)start and enable ipmi_exporter"
|
||||
ansible.builtin.systemd_service:
|
||||
name: ipmi_exporter.service
|
||||
state: restarted
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
17
roles/ipmi_exporter/meta/main.yml
Normal file
17
roles/ipmi_exporter/meta/main.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: "ipmi_exporter"
|
||||
namespace: genlab
|
||||
author: "Alexander Gorelyshev"
|
||||
company: "Genlab, LLC"
|
||||
description: ""
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.1"
|
||||
|
||||
platforms:
|
||||
- name: "Ubuntu"
|
||||
versions: ["focal", "jammy", "noble"]
|
||||
|
||||
galaxy_tags: []
|
||||
|
||||
dependencies: []
|
||||
6
roles/ipmi_exporter/molecule/default/converge.yml
Normal file
6
roles/ipmi_exporter/molecule/default/converge.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
roles:
|
||||
- role: genlab.common.ipmi_exporter
|
||||
ipmi_exp_source_dir: "molecule/default/"
|
||||
12
roles/ipmi_exporter/molecule/default/ipmi_local.conf
Normal file
12
roles/ipmi_exporter/molecule/default/ipmi_local.conf
Normal file
@@ -0,0 +1,12 @@
|
||||
modules:
|
||||
default:
|
||||
# Available collectors are bmc, bmc-watchdog, ipmi, chassis, dcmi, sel, sel-events and sm-lan-mode
|
||||
collectors:
|
||||
- bmc
|
||||
- bmc-watchdog
|
||||
- ipmi
|
||||
- dcmi
|
||||
- chassis
|
||||
- sel
|
||||
- sel-events
|
||||
- sm-lan-mode
|
||||
27
roles/ipmi_exporter/molecule/default/molecule.yml
Normal file
27
roles/ipmi_exporter/molecule/default/molecule.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: ubuntu
|
||||
image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest
|
||||
pre_build_image: true
|
||||
command: ${MOLECULE_DOCKER_COMMAND:-""}
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:rw
|
||||
cgroupns_mode: host
|
||||
privileged: true
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
lint: |
|
||||
set -e
|
||||
yamllint .
|
||||
ansible-lint .
|
||||
37
roles/ipmi_exporter/molecule/default/verify.yml
Normal file
37
roles/ipmi_exporter/molecule/default/verify.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
any_errors_fatal: true
|
||||
|
||||
tasks:
|
||||
- name: "Include default vars"
|
||||
ansible.builtin.include_vars:
|
||||
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/"
|
||||
extensions: ['yml']
|
||||
|
||||
- name: "Check if ipmi_exporter is installed"
|
||||
changed_when: false
|
||||
ansible.builtin.command: "ipmi_exporter --version"
|
||||
register: ipmi_exporter_installed_version
|
||||
|
||||
- name: "Check ipmi_exporter version"
|
||||
ansible.builtin.assert:
|
||||
that: "ipmi_exporter_installed_version.stdout is regex('{{ ipmi_exporter_version }}')"
|
||||
success_msg: "ipmi_exporter version {{ ipmi_exporter_version }} is installed and working"
|
||||
fail_msg: "ipmi_exporter version {{ ipmi_exporter_version }} is not installed or not working correctly"
|
||||
|
||||
# kics-scan ignore-block
|
||||
- name: Check if /metrics endpoint is reachable
|
||||
ansible.builtin.uri:
|
||||
url: "http://{{ ipmi_exporter_web_listen_address }}/metrics"
|
||||
return_content: true
|
||||
status_code: 200
|
||||
timeout: 60
|
||||
register: ipmi_exporter_metrics_check
|
||||
|
||||
- name: "Fail if /metrics doesn't contain ipmi_exporter_build_info line"
|
||||
ansible.builtin.assert:
|
||||
that: "'ipmi_exporter_build_info' in ipmi_exporter_metrics_check.content"
|
||||
fail_msg: "ipmi_exporter /metrics endpoint doesn't contain ipmi_exporter_build_info line!"
|
||||
success_msg: "ipmi_exporter /metrics endpoint contains ipmi_exporter_build_info line!"
|
||||
20
roles/ipmi_exporter/tasks/configure.yml
Normal file
20
roles/ipmi_exporter/tasks/configure.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
- name: "Upload ipmi_exporter local configure file"
|
||||
notify: "(Re)start and enable ipmi_exporter"
|
||||
when: ipmi_exp_source_dir is defined and ipmi_exp_source_dir | length > 0
|
||||
ansible.builtin.template:
|
||||
src: "{{ ipmi_exp_source_dir }}/ipmi_local.conf"
|
||||
dest: "{{ ipmi_exporter_config_dir }}/ipmi_local.conf"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0640'
|
||||
|
||||
- name: "Upload ipmi_exporter web configure file"
|
||||
notify: "(Re)start and enable ipmi_exporter"
|
||||
when: ipmi_exp_web_source_dir is defined and ipmi_exp_web_source_dir | length > 0
|
||||
ansible.builtin.template:
|
||||
src: "{{ ipmi_exp_web_source_dir }}/web_conf.yaml"
|
||||
dest: "{{ ipmi_exporter_config_dir }}/web_conf.yaml"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0640'
|
||||
51
roles/ipmi_exporter/tasks/install.yml
Normal file
51
roles/ipmi_exporter/tasks/install.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
- name: "Install ipmi_exporter from binary"
|
||||
block:
|
||||
- name: "Check ipmi_exporter version"
|
||||
changed_when: false
|
||||
ansible.builtin.command:
|
||||
cmd: "ipmi_exporter --version"
|
||||
register: ipmi_exporter_ver
|
||||
|
||||
- name: "Assert version correctness"
|
||||
ansible.builtin.assert:
|
||||
that: "ipmi_exporter_ver.stdout is regex('{{ ipmi_exporter_version }}')"
|
||||
success_msg: "ipmi_exporter version {{ ipmi_exporter_version }} is installed and working"
|
||||
fail_msg: "ipmi_exporter version {{ ipmi_exporter_version }} is not installed or not working correctly"
|
||||
|
||||
rescue:
|
||||
- name: "Create ipmi_exporter directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
with_items:
|
||||
- "{{ ipmi_exporter_dir }}"
|
||||
- "{{ ipmi_exporter_config_dir }}"
|
||||
|
||||
- name: "Unarchive ipmi_exporter tar file"
|
||||
notify: "(Re)start and enable ipmi_exporter"
|
||||
ansible.builtin.unarchive:
|
||||
src: "https://github.com/prometheus-community/ipmi_exporter/releases/\
|
||||
download/v{{ ipmi_exporter_version }}/ipmi_exporter-{{ ipmi_exporter_version }}.linux-amd64.tar.gz"
|
||||
dest: "{{ ipmi_exporter_dir }}"
|
||||
remote_src: true
|
||||
|
||||
- name: "Move ipmi_exporter binary"
|
||||
ansible.builtin.copy:
|
||||
src: "{{ ipmi_exporter_dir }}/ipmi_exporter-{{ ipmi_exporter_version }}.linux-amd64/ipmi_exporter"
|
||||
dest: "/usr/local/bin/ipmi_exporter"
|
||||
mode: "0755"
|
||||
owner: root
|
||||
group: root
|
||||
remote_src: true
|
||||
|
||||
- name: Create systemd service file
|
||||
ansible.builtin.template:
|
||||
src: ipmi_exporter.service.j2
|
||||
dest: /etc/systemd/system/ipmi_exporter.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
7
roles/ipmi_exporter/tasks/main.yml
Normal file
7
roles/ipmi_exporter/tasks/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
|
||||
- name: "Include installation tasks"
|
||||
ansible.builtin.include_tasks: "install.yml"
|
||||
|
||||
- name: "Including configuration tasks"
|
||||
ansible.builtin.include_tasks: "configure.yml"
|
||||
38
roles/ipmi_exporter/templates/ipmi_exporter.service.j2
Normal file
38
roles/ipmi_exporter/templates/ipmi_exporter.service.j2
Normal file
@@ -0,0 +1,38 @@
|
||||
[Unit]
|
||||
Description=IPMI exporter
|
||||
Documentation=https://github.com/prometheus-community/ipmi_exporter
|
||||
After=network.target
|
||||
StartLimitIntervalSec=120
|
||||
StartLimitBurst=5
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/ipmi_exporter \
|
||||
--web.listen-address={{ ipmi_exporter_web_listen_address }} \
|
||||
--log.level={{ ipmi_exporter_log_level }} \
|
||||
--log.format={{ ipmi_exporter_log_format }} \
|
||||
{% if ipmi_exporter_args is defined and ipmi_exporter_args | length > 0 %}
|
||||
{{ ipmi_exporter_args }} \
|
||||
{% endif %}
|
||||
{% if ipmi_exporter_source_dir is defined and ipmi_exporter_source_dir | length > 0 %}
|
||||
--config.file={{ ipmi_exporter_config_dir }}/ipmi_local.conf \
|
||||
{% endif %}
|
||||
{% if ipmi_exporter_web_source_dir is defined and ipmi_exporter_web_source_dir | length > 0 %}
|
||||
--web.config.file={{ ipmi_exporter_config_dir }}/web_conf.yaml
|
||||
{% endif %}
|
||||
|
||||
SyslogIdentifier=ipmi_exporter
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
ProtectHome=yes
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectControlGroups=true
|
||||
ProtectKernelModules=true
|
||||
ProtectKernelTunables=yes
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
1
roles/ipmi_exporter/vars/main.yml
Normal file
1
roles/ipmi_exporter/vars/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
2
roles/karma/.gitignore
vendored
Normal file
2
roles/karma/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.vscode
|
||||
.idea
|
||||
53
roles/karma/README.md
Normal file
53
roles/karma/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
genlab.karma
|
||||
=========
|
||||
This is the ansible role to install and configure Karma - alert dashboard for Prometheus Alertmanager (https://github.com/prymitive/karma)
|
||||
|
||||
------------
|
||||
⚠️ Do not forget to update:
|
||||
|
||||
- `meta/main.yml`
|
||||
- Conda/Mamba manifests
|
||||
- this README =) including *the name at the top* and *maintainers*.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
```yaml
|
||||
karma_version: "0.121" - karma version
|
||||
karma_user: karma - name of karma system user
|
||||
karma_group: karma - name of karma system group
|
||||
karma_dir: /etc/karma - path where to unpack karma
|
||||
karma_config_dir: /etc/karma/conf - where to copy configuration file
|
||||
config_source_dir: source - path to source dir with karma config on localhost
|
||||
```
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
```yaml
|
||||
roles:
|
||||
- role: genlab.karma
|
||||
karma_version: "0.121"
|
||||
config_source_dir: "karma/"
|
||||
karma_dir: "/etc/karma"
|
||||
karma_config_dir: "/etc/karma/config"
|
||||
```
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
corvus-migratorius@proton.me
|
||||
7
roles/karma/defaults/main.yml
Normal file
7
roles/karma/defaults/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
# Default variables for Karma role
|
||||
karma_version: "0.121"
|
||||
karma_user: karma
|
||||
karma_group: karma
|
||||
karma_dir: /etc/karma
|
||||
karma_config_dir: /etc/karma/conf
|
||||
7
roles/karma/handlers/main.yml
Normal file
7
roles/karma/handlers/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: "(Re)start and enable karma"
|
||||
ansible.builtin.systemd_service:
|
||||
name: karma.service
|
||||
state: restarted
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
17
roles/karma/meta/main.yml
Normal file
17
roles/karma/meta/main.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: "karma"
|
||||
namespace: genlab
|
||||
author: "Alexander Gorelyshev"
|
||||
company: "Genlab, LLC"
|
||||
description: ""
|
||||
license: "MIT"
|
||||
min_ansible_version: "2.1"
|
||||
|
||||
platforms:
|
||||
- name: "Ubuntu"
|
||||
versions: ["focal", "jammy", "noble"]
|
||||
|
||||
galaxy_tags: [monitoring]
|
||||
|
||||
dependencies: []
|
||||
9
roles/karma/molecule/default/converge.yml
Normal file
9
roles/karma/molecule/default/converge.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
gather_facts: true
|
||||
become: true
|
||||
roles:
|
||||
- role: genlab.common.karma
|
||||
karma_version: "0.121"
|
||||
config_source_dir: molecule/default/
|
||||
17
roles/karma/molecule/default/karma.conf
Normal file
17
roles/karma/molecule/default/karma.conf
Normal file
@@ -0,0 +1,17 @@
|
||||
alertmanager:
|
||||
interval: 1m
|
||||
servers:
|
||||
- name: production
|
||||
uri: http://localhost:9093
|
||||
timeout: 20s
|
||||
proxy: false
|
||||
readonly: true
|
||||
listen:
|
||||
address: "0.0.0.0"
|
||||
port: 8080
|
||||
prefix: /
|
||||
tls:
|
||||
cert: ""
|
||||
key: ""
|
||||
cors:
|
||||
allowedOrigins: []
|
||||
27
roles/karma/molecule/default/molecule.yml
Normal file
27
roles/karma/molecule/default/molecule.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: ubuntu
|
||||
image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2404}-ansible:latest
|
||||
pre_build_image: true
|
||||
command: ${MOLECULE_DOCKER_COMMAND:-""}
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:rw
|
||||
cgroupns_mode: host
|
||||
privileged: true
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
lint: |
|
||||
set -e
|
||||
yamllint .
|
||||
ansible-lint .
|
||||
35
roles/karma/molecule/default/verify.yml
Normal file
35
roles/karma/molecule/default/verify.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
any_errors_fatal: true
|
||||
vars:
|
||||
karma_version: "0.121"
|
||||
|
||||
tasks:
|
||||
- name: "Check if Karma is installed"
|
||||
changed_when: false
|
||||
ansible.builtin.command: "karma --version"
|
||||
register: karma_installed_version
|
||||
|
||||
- name: "Check Karma version"
|
||||
ansible.builtin.assert:
|
||||
that: "karma_installed_version.stdout is regex('{{ karma_version }}')"
|
||||
success_msg: "Karma version {{ karma_version }} is installed and working"
|
||||
fail_msg: "Karma version {{ karma_version }} is not installed or not working correctly"
|
||||
|
||||
# kics-scan ignore-block
|
||||
- name: "Check if Karma is reachable"
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:8080/health"
|
||||
return_content: true
|
||||
status_code: 200
|
||||
method: GET
|
||||
body_format: json
|
||||
register: karma_health
|
||||
|
||||
- name: "Assert Karma health status"
|
||||
ansible.builtin.assert:
|
||||
that: "karma_health.content.strip() == 'Pong'"
|
||||
success_msg: "Karma is healthy"
|
||||
fail_msg: "Karma is not healthy"
|
||||
10
roles/karma/tasks/configuration.yml
Normal file
10
roles/karma/tasks/configuration.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
|
||||
- name: Create karma configuration file
|
||||
notify: "(Re)start and enable karma"
|
||||
ansible.builtin.template:
|
||||
src: "{{ config_source_dir }}/karma.conf"
|
||||
dest: "{{ karma_config_dir }}/karma.conf"
|
||||
owner: "{{ karma_user }}"
|
||||
group: "{{ karma_group }}"
|
||||
mode: '0640'
|
||||
66
roles/karma/tasks/install.yml
Normal file
66
roles/karma/tasks/install.yml
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
|
||||
- name: "Create Karma system group"
|
||||
ansible.builtin.group:
|
||||
name: "{{ karma_user }}"
|
||||
system: true
|
||||
state: present
|
||||
|
||||
- name: "Create karma system user"
|
||||
ansible.builtin.user:
|
||||
name: "{{ karma_user }}"
|
||||
group: "{{ karma_group }}"
|
||||
system: true
|
||||
shell: "/sbin/nologin"
|
||||
create_home: false
|
||||
state: present
|
||||
|
||||
- name: "Install karma from binary"
|
||||
block:
|
||||
- name: "Check karma version"
|
||||
changed_when: false
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ karma_dir }}/karma-linux-amd64 --version"
|
||||
register: karma_ver
|
||||
|
||||
- name: "Assert version correctness"
|
||||
ansible.builtin.assert:
|
||||
that: "karma_ver.stdout is regex('{{ karma_version }}')"
|
||||
success_msg: "karma version {{ karma_version }} is installed and working"
|
||||
fail_msg: "karma version {{ karma_version }} is not installed or not working correctly"
|
||||
|
||||
rescue:
|
||||
- name: "Create karma directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ karma_user }}"
|
||||
group: "{{ karma_group }}"
|
||||
mode: "0755"
|
||||
with_items:
|
||||
- "{{ karma_dir }}"
|
||||
- "{{ karma_config_dir }}"
|
||||
|
||||
- name: "Unarchive karma tar file"
|
||||
notify: "(Re)start and enable karma"
|
||||
ansible.builtin.unarchive:
|
||||
src: "https://github.com/prymitive/karma/releases/download/v{{ karma_version }}/karma-linux-amd64.tar.gz"
|
||||
dest: "{{ karma_dir }}"
|
||||
remote_src: true
|
||||
|
||||
- name: "Move karma binary"
|
||||
ansible.builtin.copy:
|
||||
src: "{{ karma_dir }}/karma-linux-amd64"
|
||||
dest: "/usr/local/bin/karma"
|
||||
mode: "0755"
|
||||
owner: "{{ karma_user }}"
|
||||
group: "{{ karma_group }}"
|
||||
remote_src: true
|
||||
|
||||
- name: Create systemd service file
|
||||
ansible.builtin.template:
|
||||
src: karma.service.j2
|
||||
dest: /etc/systemd/system/karma.service
|
||||
owner: "{{ karma_user }}"
|
||||
group: "{{ karma_group }}"
|
||||
mode: '0644'
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user