From a29ff1a46a3a2ec5449163ad7dee00122c5bc765 Mon Sep 17 00:00:00 2001 From: Danyal Berchtold Date: Mon, 13 Jul 2026 12:18:39 +0200 Subject: [PATCH 1/2] feat(glpi_agent): add optional scheduled database inventory Add a systemd timer that runs `glpi-agent --partial=database` as a dedicated read-only database user, so the always-on daemon no longer connects to the local database as root on every cycle. The credential is kept in a 0600 EnvironmentFile referenced from the service unit, so it is not exposed via `systemctl show`, and the `database` inventory category is disabled on the daemon while the timer is enabled. Also add `glpi_agent__conf_no_category` to disable arbitrary inventory categories. --- CHANGELOG.md | 1 + roles/glpi_agent/README.md | 33 ++++++++ roles/glpi_agent/defaults/main.yml | 4 + roles/glpi_agent/handlers/main.yml | 5 ++ roles/glpi_agent/tasks/main.yml | 76 +++++++++++++++++++ .../glpi-agent/conf.d/z00-linuxfabrik.cfg.j2 | 6 +- .../etc/glpi-agent/db-inventory.env.j2 | 3 + .../glpi-agent-database-inventory.service.j2 | 12 +++ .../glpi-agent-database-inventory.timer.j2 | 13 ++++ 9 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 roles/glpi_agent/templates/etc/glpi-agent/db-inventory.env.j2 create mode 100644 roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.service.j2 create mode 100644 roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.timer.j2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8502e8ba3..e9d3a7914 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +* **role:glpi_agent**: Add optional scheduled database inventory. Set `glpi_agent__database_inventory_enabled` and `glpi_agent__database_inventory_credentials` to run `glpi-agent --partial=database` as a dedicated read-only database user via a systemd timer; the `database` inventory category is then disabled on the always-on daemon, which otherwise connects to the local database as root on every cycle. Also add `glpi_agent__conf_no_category` to disable arbitrary inventory categories and `glpi_agent__database_inventory_on_calendar` to set the schedule. * **role:postfix**: Add `postfix__recipient_canonicals__group_var` / `__host_var` to rewrite recipient addresses via Postfix's `recipient_canonical_maps`, mirroring the existing sender canonical rewriting. Useful for redirecting all mail addressed to a host to a central team mailbox. * **all roles**: Add `lfops__skip_restart_handlers` to deploy configuration changes without restarting the affected services, for example when a bounce has to wait for a maintenance window. Reload handlers still run, since a reload applies the configuration without an outage. Note that the skipped restart is not remembered: a later ordinary run finds the configuration already correct and does not restart either, so the service has to be restarted explicitly (`--tags :state --extra-vars '__service_state=restarted'`). See the [README](https://github.com/Linuxfabrik/lfops/blob/main/README.md#lfops__skip_restart_handlers). * **role:apache_solr, role:bind, role:blocky, role:chrony, role:clamav, role:collabora, role:coturn, role:fail2ban, role:glpi_agent, role:grafana, role:graylog_datanode, role:graylog_server, role:icinga2_master, role:icinga_kubernetes, role:icingadb, role:influxdb, role:keepalived, role:opensearch, role:php, role:redis, role:snmp, role:squid, role:vsftpd**: Add a service state variable (for example `chrony__service_state`, `clamav__clamd_service_state` or `php__fpm_service_state`) to start, stop, restart or reload the managed service independently of whether it is enabled at boot. It defaults to `started` when the role's matching `*_service_enabled` is `true` and to `stopped` otherwise, so existing inventories keep their current behaviour. diff --git a/roles/glpi_agent/README.md b/roles/glpi_agent/README.md index 03fbbe6d6..8d41a4eb8 100644 --- a/roles/glpi_agent/README.md +++ b/roles/glpi_agent/README.md @@ -25,6 +25,11 @@ Any [LFOps playbook](https://github.com/Linuxfabrik/lfops/blob/main/playbooks/RE * Deploys the configuration file. * Triggers: glpi-agent.service restart. +`glpi_agent:database_inventory` + +* Deploys the optional database inventory timer, service and credentials file. +* Triggers: systemctl daemon-reload. + `glpi_agent:state` * Manages the state of the systemd service. @@ -54,6 +59,12 @@ glpi_agent__conf_server: 'https://glpi.example.com' * Type: String. * Default: `'/tmp'` +`glpi_agent__conf_no_category` + +* List of inventory categories to disable via the `no-category` directive (for example `database` to stop the agent from inventorying local databases). When `glpi_agent__database_inventory_enabled` is `true`, the `database` category is added automatically. +* Type: List. +* Default: `[]` + `glpi_agent__conf_no_ssl_check` * Ignore self-signed certificates of the server. @@ -66,6 +77,24 @@ glpi_agent__conf_server: 'https://glpi.example.com' * Type: String. * Default: unset +`glpi_agent__database_inventory_credentials` + +* The GLPI Agent `--credentials` string used by the scheduled database inventory, for example `type:login_password,login:glpi-reader,password:...`. Since the daemon rejects `--credentials`, the database inventory runs as a separate one-shot via a systemd timer. This value is written to the `0600` environment file `/etc/glpi-agent/db-inventory.env` and referenced from the service unit via `EnvironmentFile`, so it is not exposed by `systemctl show`. Use a dedicated read-only database account and store this value in Ansible Vault. Mandatory when `glpi_agent__database_inventory_enabled` is `true`. +* Type: String. +* Default: `''` + +`glpi_agent__database_inventory_enabled` + +* Deploys a systemd timer that periodically runs `glpi-agent --partial=database` with `glpi_agent__database_inventory_credentials`, and disables the `database` category on the always-on daemon so it no longer connects to the database as root. When `false`, the timer, service and credentials file are removed. +* Type: Bool. +* Default: `false` + +`glpi_agent__database_inventory_on_calendar` + +* The `OnCalendar` schedule of the database inventory timer. Defaults to every 6 hours; the timer also applies a `RandomizedDelaySec` of 30 minutes to avoid overlapping with fixed maintenance windows such as system updates. +* Type: String. +* Default: `'*-*-* 00/6:17:00'` + `glpi_agent__service_enabled` * Enables or disables the service, analogous to `systemctl enable/disable`. @@ -88,8 +117,12 @@ Example: ```yaml # optional glpi_agent__conf_local: '/tmp' +glpi_agent__conf_no_category: [] glpi_agent__conf_no_ssl_check: false glpi_agent__conf_ssl_fingerprint: 'sha256$...' +glpi_agent__database_inventory_credentials: 'type:login_password,login:glpi-reader,password:...' +glpi_agent__database_inventory_enabled: false +glpi_agent__database_inventory_on_calendar: '*-*-* 00/6:17:00' glpi_agent__service_enabled: true glpi_agent__service_state: 'started' glpi_agent__version: 'latest' diff --git a/roles/glpi_agent/defaults/main.yml b/roles/glpi_agent/defaults/main.yml index a9687eee9..a79ba4e1f 100644 --- a/roles/glpi_agent/defaults/main.yml +++ b/roles/glpi_agent/defaults/main.yml @@ -1,5 +1,9 @@ glpi_agent__conf_local: '/tmp' +glpi_agent__conf_no_category: [] glpi_agent__conf_no_ssl_check: false +glpi_agent__database_inventory_credentials: '' +glpi_agent__database_inventory_enabled: false +glpi_agent__database_inventory_on_calendar: '*-*-* 00/6:17:00' glpi_agent__service_enabled: true glpi_agent__service_state: '{{ glpi_agent__service_enabled | bool | ternary("started", "stopped") }}' glpi_agent__version: 'latest' diff --git a/roles/glpi_agent/handlers/main.yml b/roles/glpi_agent/handlers/main.yml index 57dca65c4..40e0a31b2 100644 --- a/roles/glpi_agent/handlers/main.yml +++ b/roles/glpi_agent/handlers/main.yml @@ -12,3 +12,8 @@ - 'not (lfops__skip_restart_handlers | d(false) | bool)' - '__glpi_agent__service_state_result is not defined or __glpi_agent__service_state_result is not changed' - 'glpi_agent__service_state != "stopped"' + + +- name: 'glpi_agent: systemctl daemon-reload' + ansible.builtin.systemd: + daemon_reload: true diff --git a/roles/glpi_agent/tasks/main.yml b/roles/glpi_agent/tasks/main.yml index 53eef187d..38ed8d3e5 100644 --- a/roles/glpi_agent/tasks/main.yml +++ b/roles/glpi_agent/tasks/main.yml @@ -74,6 +74,82 @@ - 'glpi_agent:configure' +- block: + + - name: 'Assert that the database inventory credentials are set' + ansible.builtin.assert: + that: + - 'glpi_agent__database_inventory_credentials | length > 0' + fail_msg: 'glpi_agent__database_inventory_credentials must be set when glpi_agent__database_inventory_enabled is true.' + when: 'glpi_agent__database_inventory_enabled | bool' + + - name: 'Deploy /etc/glpi-agent/db-inventory.env' + ansible.builtin.template: + backup: false # contains secrets + src: 'etc/glpi-agent/db-inventory.env.j2' + dest: '/etc/glpi-agent/db-inventory.env' + owner: 'root' + group: 'root' + mode: 0o600 # contains secrets + when: 'glpi_agent__database_inventory_enabled | bool' + + - name: 'Deploy /etc/systemd/system/glpi-agent-database-inventory.service' + ansible.builtin.template: + backup: true + src: 'etc/systemd/system/glpi-agent-database-inventory.service.j2' + dest: '/etc/systemd/system/glpi-agent-database-inventory.service' + owner: 'root' + group: 'root' + mode: 0o644 + when: 'glpi_agent__database_inventory_enabled | bool' + notify: 'glpi_agent: systemctl daemon-reload' + + - name: 'Deploy /etc/systemd/system/glpi-agent-database-inventory.timer' + ansible.builtin.template: + backup: true + src: 'etc/systemd/system/glpi-agent-database-inventory.timer.j2' + dest: '/etc/systemd/system/glpi-agent-database-inventory.timer' + owner: 'root' + group: 'root' + mode: 0o644 + when: 'glpi_agent__database_inventory_enabled | bool' + notify: 'glpi_agent: systemctl daemon-reload' + + - name: 'systemctl enable and start glpi-agent-database-inventory.timer' + ansible.builtin.systemd: + daemon_reload: true + enabled: true + name: 'glpi-agent-database-inventory.timer' + state: 'started' + when: 'glpi_agent__database_inventory_enabled | bool' + + - name: 'systemctl stop and disable glpi-agent-database-inventory.timer' + ansible.builtin.systemd: + enabled: false + name: 'glpi-agent-database-inventory.timer' + state: 'stopped' + register: '__glpi_agent__database_inventory_timer_result' + failed_when: + - '__glpi_agent__database_inventory_timer_result is failed' + - '"Could not find" not in (__glpi_agent__database_inventory_timer_result["msg"] | d(""))' + when: 'not (glpi_agent__database_inventory_enabled | bool)' + + - name: 'Remove the database inventory units and environment file' + ansible.builtin.file: + path: '{{ item }}' + state: 'absent' + loop: + - '/etc/glpi-agent/db-inventory.env' + - '/etc/systemd/system/glpi-agent-database-inventory.service' + - '/etc/systemd/system/glpi-agent-database-inventory.timer' + when: 'not (glpi_agent__database_inventory_enabled | bool)' + notify: 'glpi_agent: systemctl daemon-reload' + + tags: + - 'glpi_agent' + - 'glpi_agent:database_inventory' + + - block: - name: 'systemctl {{ glpi_agent__service_enabled | bool | ternary("enable", "disable") }} glpi-agent.service' diff --git a/roles/glpi_agent/templates/etc/glpi-agent/conf.d/z00-linuxfabrik.cfg.j2 b/roles/glpi_agent/templates/etc/glpi-agent/conf.d/z00-linuxfabrik.cfg.j2 index 786358eab..9cb53acb3 100644 --- a/roles/glpi_agent/templates/etc/glpi-agent/conf.d/z00-linuxfabrik.cfg.j2 +++ b/roles/glpi_agent/templates/etc/glpi-agent/conf.d/z00-linuxfabrik.cfg.j2 @@ -1,7 +1,11 @@ # {{ ansible_managed }} -# 2024101701 +# 2026071301 +{% set glpi_agent__no_category = (glpi_agent__conf_no_category + (['database'] if glpi_agent__database_inventory_enabled | bool else [])) | unique | sort %} local = {{ glpi_agent__conf_local }} +{% if glpi_agent__no_category | length %} +no-category = {{ glpi_agent__no_category | join(',') }} +{% endif %} no-ssl-check = {{ glpi_agent__conf_no_ssl_check | ternary('1', '0') }} server = {{ glpi_agent__conf_server }} {% if glpi_agent__conf_ssl_fingerprint is defined and glpi_agent__conf_ssl_fingerprint | length %} diff --git a/roles/glpi_agent/templates/etc/glpi-agent/db-inventory.env.j2 b/roles/glpi_agent/templates/etc/glpi-agent/db-inventory.env.j2 new file mode 100644 index 000000000..61499f48d --- /dev/null +++ b/roles/glpi_agent/templates/etc/glpi-agent/db-inventory.env.j2 @@ -0,0 +1,3 @@ +# {{ ansible_managed }} +# 2026071301 +GLPI_DB_CRED={{ glpi_agent__database_inventory_credentials }} diff --git a/roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.service.j2 b/roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.service.j2 new file mode 100644 index 000000000..5bc05626a --- /dev/null +++ b/roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.service.j2 @@ -0,0 +1,12 @@ +# {{ ansible_managed }} +# 2026071301 + +[Unit] +Description=GLPI Agent database inventory +After=network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +EnvironmentFile=/etc/glpi-agent/db-inventory.env +ExecStart=/usr/bin/glpi-agent --credentials=${GLPI_DB_CRED} --partial=database diff --git a/roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.timer.j2 b/roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.timer.j2 new file mode 100644 index 000000000..1160a6d59 --- /dev/null +++ b/roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.timer.j2 @@ -0,0 +1,13 @@ +# {{ ansible_managed }} +# 2026071301 + +[Unit] +Description=GLPI Agent database inventory timer + +[Timer] +OnCalendar={{ glpi_agent__database_inventory_on_calendar }} +Persistent=true +RandomizedDelaySec=30m + +[Install] +WantedBy=timers.target From e9c53c14fde8ec5614b90c016329732de07282da Mon Sep 17 00:00:00 2001 From: Navid Sassan Date: Thu, 30 Jul 2026 15:30:29 +0200 Subject: [PATCH 2/2] fix(roles/glpi_agent): run the database inventory with its own config file --- CHANGELOG.md | 2 +- roles/glpi_agent/README.md | 33 ++++++--- roles/glpi_agent/defaults/main.yml | 1 - roles/glpi_agent/tasks/main.yml | 70 ++++++++++++++----- .../glpi-agent/conf.d/z00-linuxfabrik.cfg.j2 | 18 +++-- .../etc/glpi-agent/db-inventory.env.j2 | 14 +++- .../glpi-agent-database-inventory.service.j2 | 4 +- 7 files changed, 105 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9d3a7914..337f077f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -* **role:glpi_agent**: Add optional scheduled database inventory. Set `glpi_agent__database_inventory_enabled` and `glpi_agent__database_inventory_credentials` to run `glpi-agent --partial=database` as a dedicated read-only database user via a systemd timer; the `database` inventory category is then disabled on the always-on daemon, which otherwise connects to the local database as root on every cycle. Also add `glpi_agent__conf_no_category` to disable arbitrary inventory categories and `glpi_agent__database_inventory_on_calendar` to set the schedule. +* **role:glpi_agent**: Add optional scheduled database inventory. Set `glpi_agent__database_inventory_enabled` and `glpi_agent__database_inventory_login` to run `glpi-agent --partial=database` as a dedicated read-only database user via a systemd timer; the `database` inventory category is then disabled on the always-on daemon, which otherwise connects to the local database as root on every cycle. Also add `glpi_agent__conf_no_category` to disable arbitrary inventory categories and `glpi_agent__database_inventory_on_calendar` to set the schedule. * **role:postfix**: Add `postfix__recipient_canonicals__group_var` / `__host_var` to rewrite recipient addresses via Postfix's `recipient_canonical_maps`, mirroring the existing sender canonical rewriting. Useful for redirecting all mail addressed to a host to a central team mailbox. * **all roles**: Add `lfops__skip_restart_handlers` to deploy configuration changes without restarting the affected services, for example when a bounce has to wait for a maintenance window. Reload handlers still run, since a reload applies the configuration without an outage. Note that the skipped restart is not remembered: a later ordinary run finds the configuration already correct and does not restart either, so the service has to be restarted explicitly (`--tags :state --extra-vars '__service_state=restarted'`). See the [README](https://github.com/Linuxfabrik/lfops/blob/main/README.md#lfops__skip_restart_handlers). * **role:apache_solr, role:bind, role:blocky, role:chrony, role:clamav, role:collabora, role:coturn, role:fail2ban, role:glpi_agent, role:grafana, role:graylog_datanode, role:graylog_server, role:icinga2_master, role:icinga_kubernetes, role:icingadb, role:influxdb, role:keepalived, role:opensearch, role:php, role:redis, role:snmp, role:squid, role:vsftpd**: Add a service state variable (for example `chrony__service_state`, `clamav__clamd_service_state` or `php__fpm_service_state`) to start, stop, restart or reload the managed service independently of whether it is enabled at boot. It defaults to `started` when the role's matching `*_service_enabled` is `true` and to `stopped` otherwise, so existing inventories keep their current behaviour. diff --git a/roles/glpi_agent/README.md b/roles/glpi_agent/README.md index 8d41a4eb8..c8e5ef690 100644 --- a/roles/glpi_agent/README.md +++ b/roles/glpi_agent/README.md @@ -6,6 +6,17 @@ This role installs and configures the [GLPI Agent](https://glpi-agent.readthedoc *Available since LFOps `3.0.0`.* +## How the Role Behaves + +The scheduled database inventory runs as a one-shot via a systemd timer instead of as part of the always-on daemon, because the agent refuses `--credentials` in daemon mode and offers no equivalent configuration directive. + +Enabling the scheduled inventory also disables the `database` category on the always-on daemon. Without credentials the daemon falls back to its default database access (root via the local socket) on every cycle, which the dedicated account is meant to replace. + +The one-shot must not inherit that `no-category` setting, otherwise the agent would discard its own `--partial=database` run. It therefore gets its own configuration file at `/etc/glpi-agent/db-inventory.cfg`, passed via `--conf-file`, which carries the same settings without the `no-category` directive. Both files are rendered from the same template, so they cannot drift apart. Note that the file lives outside `conf.d`, since the daemon would otherwise read it too. + +The credentials are assembled from `glpi_agent__database_inventory_login` into the `0600` environment file `/etc/glpi-agent/db-inventory.env` and pulled into the unit via `EnvironmentFile`, so they are not part of the unit file itself. systemd expands the variable into the command line when it starts the job, so the password is readable in the process list for as long as the one-shot runs. Use a dedicated read-only database account, and keep the password in Ansible Vault. + + ## Dependent Roles Any [LFOps playbook](https://github.com/Linuxfabrik/lfops/blob/main/playbooks/README.md) that installs this role runs these for you. Optional ones can be disabled via the playbook's skip variables. @@ -22,7 +33,7 @@ Any [LFOps playbook](https://github.com/Linuxfabrik/lfops/blob/main/playbooks/RE `glpi_agent:configure` -* Deploys the configuration file. +* Deploys the configuration files. * Triggers: glpi-agent.service restart. `glpi_agent:database_inventory` @@ -77,21 +88,21 @@ glpi_agent__conf_server: 'https://glpi.example.com' * Type: String. * Default: unset -`glpi_agent__database_inventory_credentials` - -* The GLPI Agent `--credentials` string used by the scheduled database inventory, for example `type:login_password,login:glpi-reader,password:...`. Since the daemon rejects `--credentials`, the database inventory runs as a separate one-shot via a systemd timer. This value is written to the `0600` environment file `/etc/glpi-agent/db-inventory.env` and referenced from the service unit via `EnvironmentFile`, so it is not exposed by `systemctl show`. Use a dedicated read-only database account and store this value in Ansible Vault. Mandatory when `glpi_agent__database_inventory_enabled` is `true`. -* Type: String. -* Default: `''` - `glpi_agent__database_inventory_enabled` -* Deploys a systemd timer that periodically runs `glpi-agent --partial=database` with `glpi_agent__database_inventory_credentials`, and disables the `database` category on the always-on daemon so it no longer connects to the database as root. When `false`, the timer, service and credentials file are removed. +* Deploys a systemd timer that periodically runs `glpi-agent --partial=database`, and disables the `database` category on the always-on daemon. When `false`, the timer, service, configuration and environment file are removed. * Type: Bool. * Default: `false` +`glpi_agent__database_inventory_login` + +* The database account used by the scheduled database inventory. Mandatory when `glpi_agent__database_inventory_enabled` is `true`. +* Type: Dictionary. +* Default: none + `glpi_agent__database_inventory_on_calendar` -* The `OnCalendar` schedule of the database inventory timer. Defaults to every 6 hours; the timer also applies a `RandomizedDelaySec` of 30 minutes to avoid overlapping with fixed maintenance windows such as system updates. +* The `OnCalendar` schedule of the database inventory timer. The timer also applies a `RandomizedDelaySec` of 30 minutes, so the job does not collide with fixed maintenance windows such as system updates. * Type: String. * Default: `'*-*-* 00/6:17:00'` @@ -120,8 +131,10 @@ glpi_agent__conf_local: '/tmp' glpi_agent__conf_no_category: [] glpi_agent__conf_no_ssl_check: false glpi_agent__conf_ssl_fingerprint: 'sha256$...' -glpi_agent__database_inventory_credentials: 'type:login_password,login:glpi-reader,password:...' glpi_agent__database_inventory_enabled: false +glpi_agent__database_inventory_login: + username: 'glpi-reader' + password: 'linuxfabrik' glpi_agent__database_inventory_on_calendar: '*-*-* 00/6:17:00' glpi_agent__service_enabled: true glpi_agent__service_state: 'started' diff --git a/roles/glpi_agent/defaults/main.yml b/roles/glpi_agent/defaults/main.yml index a79ba4e1f..7b23f0a25 100644 --- a/roles/glpi_agent/defaults/main.yml +++ b/roles/glpi_agent/defaults/main.yml @@ -1,7 +1,6 @@ glpi_agent__conf_local: '/tmp' glpi_agent__conf_no_category: [] glpi_agent__conf_no_ssl_check: false -glpi_agent__database_inventory_credentials: '' glpi_agent__database_inventory_enabled: false glpi_agent__database_inventory_on_calendar: '*-*-* 00/6:17:00' glpi_agent__service_enabled: true diff --git a/roles/glpi_agent/tasks/main.yml b/roles/glpi_agent/tasks/main.yml index 38ed8d3e5..3a7f54c7e 100644 --- a/roles/glpi_agent/tasks/main.yml +++ b/roles/glpi_agent/tasks/main.yml @@ -1,3 +1,21 @@ +- block: + + - name: 'Check variable constraints' + ansible.builtin.assert: + that: + - 'glpi_agent__database_inventory_login is defined' + - 'glpi_agent__database_inventory_login["username"] | d("") | length > 0' + - 'glpi_agent__database_inventory_login["password"] | d("") | length > 0' + quiet: true + fail_msg: 'glpi_agent__database_inventory_login needs a "username" and a "password" when glpi_agent__database_inventory_enabled is true.' + when: + - 'glpi_agent__database_inventory_enabled | bool' + + tags: + # use 'always' so the validation runs even when the role runs with a specific tag. + - 'always' + + - block: - name: 'Store the github release version' @@ -67,22 +85,35 @@ owner: 'root' group: 'root' mode: 0o640 + vars: + __glpi_agent__render_no_category: true notify: 'glpi_agent: restart glpi-agent' + # the database inventory one-shot is started with --conf-file and therefore reads this file + # instead of agent.cfg and its conf.d include, so that it does not inherit + # "no-category = database". deployed here and not under 'glpi_agent:database_inventory', so + # that a configure run keeps both files in sync. + - name: 'Deploy /etc/glpi-agent/db-inventory.cfg' + ansible.builtin.template: + backup: true + src: 'etc/glpi-agent/conf.d/z00-linuxfabrik.cfg.j2' + dest: '/etc/glpi-agent/db-inventory.cfg' + owner: 'root' + group: 'root' + mode: 0o640 + vars: + __glpi_agent__render_no_category: false + when: + - 'glpi_agent__database_inventory_enabled | bool' + tags: - 'glpi_agent' - 'glpi_agent:configure' +# glpi_agent__database_inventory_enabled == true - block: - - name: 'Assert that the database inventory credentials are set' - ansible.builtin.assert: - that: - - 'glpi_agent__database_inventory_credentials | length > 0' - fail_msg: 'glpi_agent__database_inventory_credentials must be set when glpi_agent__database_inventory_enabled is true.' - when: 'glpi_agent__database_inventory_enabled | bool' - - name: 'Deploy /etc/glpi-agent/db-inventory.env' ansible.builtin.template: backup: false # contains secrets @@ -91,7 +122,6 @@ owner: 'root' group: 'root' mode: 0o600 # contains secrets - when: 'glpi_agent__database_inventory_enabled | bool' - name: 'Deploy /etc/systemd/system/glpi-agent-database-inventory.service' ansible.builtin.template: @@ -101,7 +131,6 @@ owner: 'root' group: 'root' mode: 0o644 - when: 'glpi_agent__database_inventory_enabled | bool' notify: 'glpi_agent: systemctl daemon-reload' - name: 'Deploy /etc/systemd/system/glpi-agent-database-inventory.timer' @@ -112,18 +141,26 @@ owner: 'root' group: 'root' mode: 0o644 - when: 'glpi_agent__database_inventory_enabled | bool' notify: 'glpi_agent: systemctl daemon-reload' - - name: 'systemctl enable and start glpi-agent-database-inventory.timer' + - name: 'systemctl enable --now glpi-agent-database-inventory.timer' ansible.builtin.systemd: daemon_reload: true enabled: true name: 'glpi-agent-database-inventory.timer' state: 'started' - when: 'glpi_agent__database_inventory_enabled | bool' - - name: 'systemctl stop and disable glpi-agent-database-inventory.timer' + # block + when: 'glpi_agent__database_inventory_enabled | bool' + tags: + - 'glpi_agent' + - 'glpi_agent:database_inventory' + + +# glpi_agent__database_inventory_enabled == false +- block: + + - name: 'systemctl disable --now glpi-agent-database-inventory.timer' ansible.builtin.systemd: enabled: false name: 'glpi-agent-database-inventory.timer' @@ -132,19 +169,20 @@ failed_when: - '__glpi_agent__database_inventory_timer_result is failed' - '"Could not find" not in (__glpi_agent__database_inventory_timer_result["msg"] | d(""))' - when: 'not (glpi_agent__database_inventory_enabled | bool)' - - name: 'Remove the database inventory units and environment file' + - name: 'rm -f {{ item }}' ansible.builtin.file: path: '{{ item }}' state: 'absent' loop: + - '/etc/glpi-agent/db-inventory.cfg' - '/etc/glpi-agent/db-inventory.env' - '/etc/systemd/system/glpi-agent-database-inventory.service' - '/etc/systemd/system/glpi-agent-database-inventory.timer' - when: 'not (glpi_agent__database_inventory_enabled | bool)' notify: 'glpi_agent: systemctl daemon-reload' + # block + when: 'not (glpi_agent__database_inventory_enabled | bool)' tags: - 'glpi_agent' - 'glpi_agent:database_inventory' diff --git a/roles/glpi_agent/templates/etc/glpi-agent/conf.d/z00-linuxfabrik.cfg.j2 b/roles/glpi_agent/templates/etc/glpi-agent/conf.d/z00-linuxfabrik.cfg.j2 index 9cb53acb3..d9509563a 100644 --- a/roles/glpi_agent/templates/etc/glpi-agent/conf.d/z00-linuxfabrik.cfg.j2 +++ b/roles/glpi_agent/templates/etc/glpi-agent/conf.d/z00-linuxfabrik.cfg.j2 @@ -1,13 +1,21 @@ # {{ ansible_managed }} -# 2026071301 +# 2026072801 +{# + This template is rendered to two destinations: conf.d/z00-linuxfabrik.cfg for the always-on + daemon, and db-inventory.cfg for the scheduled database inventory one-shot, which is started + with --conf-file so it reads the latter instead of agent.cfg and its conf.d include. + The one-shot must not inherit "no-category = database": the agent validates a partial + inventory against the disabled categories and would filter its own --partial=database run + away ("Nothing to inventory on partial inventory event"). Hence the render flag below. +#} -{% set glpi_agent__no_category = (glpi_agent__conf_no_category + (['database'] if glpi_agent__database_inventory_enabled | bool else [])) | unique | sort %} +{% set __glpi_agent__no_category = (glpi_agent__conf_no_category + (['database'] if glpi_agent__database_inventory_enabled | bool else [])) | unique | sort %} local = {{ glpi_agent__conf_local }} -{% if glpi_agent__no_category | length %} -no-category = {{ glpi_agent__no_category | join(',') }} +{% if __glpi_agent__render_no_category | bool and __glpi_agent__no_category | length > 0 %} +no-category = {{ __glpi_agent__no_category | join(',') }} {% endif %} no-ssl-check = {{ glpi_agent__conf_no_ssl_check | ternary('1', '0') }} server = {{ glpi_agent__conf_server }} -{% if glpi_agent__conf_ssl_fingerprint is defined and glpi_agent__conf_ssl_fingerprint | length %} +{% if glpi_agent__conf_ssl_fingerprint is defined and glpi_agent__conf_ssl_fingerprint | length > 0 %} ssl-fingerprint = {{ glpi_agent__conf_ssl_fingerprint }} {% endif %} diff --git a/roles/glpi_agent/templates/etc/glpi-agent/db-inventory.env.j2 b/roles/glpi_agent/templates/etc/glpi-agent/db-inventory.env.j2 index 61499f48d..ba51c2be6 100644 --- a/roles/glpi_agent/templates/etc/glpi-agent/db-inventory.env.j2 +++ b/roles/glpi_agent/templates/etc/glpi-agent/db-inventory.env.j2 @@ -1,3 +1,13 @@ # {{ ansible_managed }} -# 2026071301 -GLPI_DB_CRED={{ glpi_agent__database_inventory_credentials }} +# 2026072801 +{# + Two layers of escaping, applied in this order: + 1. The agent parses --credentials as comma-separated "key:value" pairs, so an unquoted value + containing a comma is silently truncated. Single-quote both values and escape an embedded + single quote as \', which is what the agent's parser expects. + 2. systemd unescapes backslashes when it reads this file, so every backslash has to be doubled + here to survive into the command line. +-#} +{% set __glpi_agent__username = glpi_agent__database_inventory_login['username'] | replace("'", "\\'") | replace("\\", "\\\\") %} +{% set __glpi_agent__password = glpi_agent__database_inventory_login['password'] | replace("'", "\\'") | replace("\\", "\\\\") %} +GLPI_DB_CRED=type:login_password,login:'{{ __glpi_agent__username }}',password:'{{ __glpi_agent__password }}' diff --git a/roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.service.j2 b/roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.service.j2 index 5bc05626a..6d1dc529f 100644 --- a/roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.service.j2 +++ b/roles/glpi_agent/templates/etc/systemd/system/glpi-agent-database-inventory.service.j2 @@ -1,5 +1,5 @@ # {{ ansible_managed }} -# 2026071301 +# 2026072801 [Unit] Description=GLPI Agent database inventory @@ -9,4 +9,4 @@ Wants=network-online.target [Service] Type=oneshot EnvironmentFile=/etc/glpi-agent/db-inventory.env -ExecStart=/usr/bin/glpi-agent --credentials=${GLPI_DB_CRED} --partial=database +ExecStart=/usr/bin/glpi-agent --conf-file=/etc/glpi-agent/db-inventory.cfg --credentials=${GLPI_DB_CRED} --partial=database