diff --git a/roles/ipmi_exporter/defaults/main.yml b/roles/ipmi_exporter/defaults/main.yml index 6c550381b..c86a44c43 100644 --- a/roles/ipmi_exporter/defaults/main.yml +++ b/roles/ipmi_exporter/defaults/main.yml @@ -30,3 +30,6 @@ ipmi_exporter_config_dir: /etc/ipmi_exporter # Local path to stash and extract the archive ipmi_exporter_local_cache_path: "/tmp/ipmi_exporter-{{ ansible_facts['system'] | lower }}-{{ _ipmi_exporter_go_ansible_arch }}/{{ ipmi_exporter_version }}" + +ipmi_exporter_udev_enable: false +ipmi_exporter_udev_rule_path: "/etc/udev/rules.d/99-ipmi-exporter.rules" diff --git a/roles/ipmi_exporter/handlers/main.yml b/roles/ipmi_exporter/handlers/main.yml index ef5827984..90c8cd9b8 100644 --- a/roles/ipmi_exporter/handlers/main.yml +++ b/roles/ipmi_exporter/handlers/main.yml @@ -8,3 +8,12 @@ state: restarted when: - not ansible_check_mode + +- name: Reload udev rules + listen: "reload udev rules" + become: true + ansible.builtin.command: + cmd: udevadm control --reload-rules && udevadm trigger --subsystem-match=ipmi + changed_when: false + when: + - not ansible_check_mode diff --git a/roles/ipmi_exporter/meta/argument_specs.yml b/roles/ipmi_exporter/meta/argument_specs.yml index 275dd26cb..13a595cad 100644 --- a/roles/ipmi_exporter/meta/argument_specs.yml +++ b/roles/ipmi_exporter/meta/argument_specs.yml @@ -65,3 +65,12 @@ argument_specs: ipmi_exporter_config_dir: description: "Path to directory with ipmi_exporter configuration" default: "/etc/ipmi_exporter" + ipmi_exporter_udev_enable: + description: + - "When true, deploy a udev rule granting the service group read/write access to /dev/ipmi* devices." + - "This allows freeipmi tools to access IPMI hardware without sudo." + type: "bool" + default: false + ipmi_exporter_udev_rule_path: + description: "Path where the udev rule file is deployed when ipmi_exporter_udev_enable is true." + default: "/etc/udev/rules.d/99-ipmi-exporter.rules" diff --git a/roles/ipmi_exporter/molecule/capabilities/molecule.yml b/roles/ipmi_exporter/molecule/capabilities/molecule.yml new file mode 100644 index 000000000..1491c26de --- /dev/null +++ b/roles/ipmi_exporter/molecule/capabilities/molecule.yml @@ -0,0 +1,7 @@ +--- +provisioner: + inventory: + group_vars: + all: + ipmi_exporter_web_listen_address: "127.0.0.1:9290" + ipmi_exporter_udev_enable: true diff --git a/roles/ipmi_exporter/molecule/capabilities/tests/test_capabilities.py b/roles/ipmi_exporter/molecule/capabilities/tests/test_capabilities.py new file mode 100644 index 000000000..9749f8e80 --- /dev/null +++ b/roles/ipmi_exporter/molecule/capabilities/tests/test_capabilities.py @@ -0,0 +1,48 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from testinfra_helpers import get_target_hosts + +testinfra_hosts = get_target_hosts() + + +def test_udev_rule_exists(host): + f = host.file("/etc/udev/rules.d/99-ipmi-exporter.rules") + assert f.exists + assert f.is_file + + +def test_udev_rule_permissions(host): + f = host.file("/etc/udev/rules.d/99-ipmi-exporter.rules") + assert f.user == "root" + assert f.group == "root" + assert f.mode == 0o644 + + +def test_udev_rule_content(host): + f = host.file("/etc/udev/rules.d/99-ipmi-exporter.rules") + assert 'KERNEL=="ipmi*"' in f.content_string + assert 'SUBSYSTEM=="ipmi"' in f.content_string + assert 'GROUP="ipmi-exp"' in f.content_string + assert 'MODE="0660"' in f.content_string + + +def test_service(host): + s = host.service("ipmi_exporter") + try: + assert s.is_running + except AssertionError: + journal_output = host.run('journalctl -u ipmi_exporter --since "1 hour ago"') + print("\n==== journalctl -u ipmi_exporter Output ====\n") + print(journal_output) + print("\n============================================\n") + raise + + +def test_socket(host): + sockets = [ + "tcp://127.0.0.1:9290" + ] + for socket in sockets: + s = host.socket(socket) + assert s.is_listening diff --git a/roles/ipmi_exporter/tasks/configure.yml b/roles/ipmi_exporter/tasks/configure.yml index 25cc671ed..58dce782a 100644 --- a/roles/ipmi_exporter/tasks/configure.yml +++ b/roles/ipmi_exporter/tasks/configure.yml @@ -15,6 +15,35 @@ - configure - ipmi_exporter_configure +- name: Deploy udev rule for IPMI device access + ansible.builtin.template: + src: ipmi_udev.rules.j2 + dest: "{{ ipmi_exporter_udev_rule_path }}" + owner: root + group: root + mode: 0644 + become: true + when: ipmi_exporter_udev_enable + notify: + - reload udev rules + tags: + - ipmi_exporter + - configure + - ipmi_exporter_configure + +- name: Remove udev rule for IPMI device access + ansible.builtin.file: + path: "{{ ipmi_exporter_udev_rule_path }}" + state: absent + become: true + when: not ipmi_exporter_udev_enable + notify: + - reload udev rules + tags: + - ipmi_exporter + - configure + - ipmi_exporter_configure + - name: Copy the ipmi_exporter config file ansible.builtin.template: src: config.yaml.j2 diff --git a/roles/ipmi_exporter/templates/ipmi_udev.rules.j2 b/roles/ipmi_exporter/templates/ipmi_udev.rules.j2 new file mode 100644 index 000000000..9e9ef0c4a --- /dev/null +++ b/roles/ipmi_exporter/templates/ipmi_udev.rules.j2 @@ -0,0 +1,3 @@ +{{ ansible_managed | comment }} + +KERNEL=="ipmi*", SUBSYSTEM=="ipmi", GROUP="{{ ipmi_exporter_system_group }}", MODE="0660"