├── .ansible-lint ├── .gitignore ├── handlers └── main.yml ├── vars ├── Debian.yml └── RedHat.yml ├── .github ├── FUNDING.yml └── workflows │ ├── release.yml │ ├── stale.yml │ └── ci.yml ├── .yamllint ├── templates ├── plugin-conf.j2 └── munin-node.conf.j2 ├── meta └── main.yml ├── molecule └── default │ ├── molecule.yml │ ├── converge.yml │ └── playbook-vars.yml ├── defaults └── main.yml ├── LICENSE ├── tasks └── main.yml └── README.md /.ansible-lint: -------------------------------------------------------------------------------- 1 | skip_list: 2 | - 'yaml' 3 | - 'role-name' 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.retry 2 | */__pycache__ 3 | *.pyc 4 | .cache 5 | 6 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart munin-node 3 | service: name=munin-node state=restarted 4 | -------------------------------------------------------------------------------- /vars/Debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | munin_node_log: /var/log/munin/munin-node.log 3 | munin_node_pid: /var/run/munin/munin-node.pid 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | --- 3 | github: geerlingguy 4 | patreon: geerlingguy 5 | -------------------------------------------------------------------------------- /vars/RedHat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | munin_node_log: /var/log/munin-node/munin-node.log 3 | munin_node_pid: /var/run/munin/munin-node.pid 4 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | line-length: 6 | max: 120 7 | level: warning 8 | 9 | ignore: | 10 | .github/workflows/stale.yml 11 | -------------------------------------------------------------------------------- /templates/plugin-conf.j2: -------------------------------------------------------------------------------- 1 | {% for section,directives in munin_node_config.items() | list %} 2 | [{{section}}] 3 | {% for name,val in directives.items() | list %} 4 | {{ name }} {{ val }} 5 | {% endfor %} 6 | 7 | {% endfor %} 8 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: [] 3 | 4 | galaxy_info: 5 | role_name: munin-node 6 | author: geerlingguy 7 | description: Munin node monitoring endpoint for RedHat/CentOS or Debian/Ubuntu. 8 | company: "Midwestern Mac, LLC" 9 | license: "license (BSD, MIT)" 10 | min_ansible_version: 2.10 11 | platforms: 12 | - name: Debian 13 | versions: 14 | - all 15 | - name: Ubuntu 16 | versions: 17 | - all 18 | galaxy_tags: 19 | - monitoring 20 | - system 21 | - munin 22 | - metrics 23 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | role_name_check: 1 3 | dependency: 4 | name: galaxy 5 | options: 6 | ignore-errors: true 7 | driver: 8 | name: docker 9 | platforms: 10 | - name: instance 11 | image: "geerlingguy/docker-${MOLECULE_DISTRO:-rockylinux9}-ansible:latest" 12 | command: ${MOLECULE_DOCKER_COMMAND:-""} 13 | volumes: 14 | - /sys/fs/cgroup:/sys/fs/cgroup:rw 15 | cgroupns_mode: host 16 | privileged: true 17 | pre_build_image: true 18 | provisioner: 19 | name: ansible 20 | playbooks: 21 | converge: ${MOLECULE_PLAYBOOK:-converge.yml} 22 | -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | #become: true 5 | 6 | pre_tasks: 7 | - name: Update apt cache. 8 | apt: 9 | update_cache: true 10 | cache_valid_time: 600 11 | when: ansible_facts.os_family == 'Debian' 12 | changed_when: false 13 | 14 | - name: Install test dependencies (Debian). 15 | package: 16 | name: netcat-traditional 17 | state: present 18 | when: ansible_facts.os_family == 'Debian' 19 | 20 | - name: Install test dependencies (RedHat). 21 | package: 22 | name: nc 23 | state: present 24 | when: ansible_facts.os_family == 'RedHat' 25 | 26 | roles: 27 | - role: geerlingguy.munin-node 28 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | munin_node_bind_host: "*" 3 | munin_node_bind_port: "4949" 4 | 5 | munin_node_host_name: '' 6 | 7 | # Munin requires IPs be added as regular expressions. 8 | munin_node_allowed_ips: 9 | - '^127\.0\.0\.1$' 10 | - '^::1$' 11 | 12 | munin_node_allowed_cidrs: [] 13 | munin_node_denied_cidrs: [] 14 | 15 | # Source and destination of munin plugins. 16 | munin_plugin_src_path: /usr/share/munin/plugins/ 17 | munin_plugin_dest_path: /etc/munin/plugins/ 18 | 19 | # List of munin plugins to enable. 20 | munin_node_plugins: [] 21 | # - name: uptime 22 | # - name: if_eth0 23 | # plugin: if_ 24 | # - name: ps_test 25 | # plugin: ps_ 26 | 27 | # List of munin plugins to install. 28 | munin_node_install_plugins: [] 29 | # - src: files/munin/redis_ 30 | # - remote_src: https://raw.githubusercontent.com/ohitz/phpfpm-multi-munin-plugin/master/phpfpm-multi 31 | 32 | # Plugin configuration options (the key is the plugin heading, items within will 33 | # be options for the plugin). 34 | munin_node_config: { 35 | # "ps_test": { 36 | # "env.regex": "bash", 37 | # "env.name": "bash" 38 | # } 39 | } 40 | -------------------------------------------------------------------------------- /molecule/default/playbook-vars.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | #become: true 5 | 6 | vars: 7 | munin_node_allowed_cidrs: ['10.0.0.0/8', '2001:db8::/32'] 8 | munin_node_denied_cidrs: ['10.42.0.0/16', '2001:db8:42::/48'] 9 | munin_node_plugins: 10 | - name: uptime 11 | - name: if_eth1 12 | plugin: if_ 13 | 14 | munin_node_config: { 15 | "ps_test": { 16 | "env.regex": "bash", 17 | "env.name": "bash" 18 | } 19 | } 20 | 21 | pre_tasks: 22 | - name: Update apt cache. 23 | apt: 24 | update_cache: true 25 | cache_valid_time: 600 26 | when: ansible_facts.os_family == 'Debian' 27 | changed_when: false 28 | 29 | - name: Install test dependencies (Debian). 30 | package: 31 | name: netcat 32 | state: present 33 | when: ansible_facts.os_family == 'Debian' 34 | 35 | - name: Install test dependencies (RedHat). 36 | package: 37 | name: nc 38 | state: present 39 | when: ansible_facts.os_family == 'RedHat' 40 | 41 | roles: 42 | - role: geerlingguy.munin-node 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Jeff Geerling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This workflow requires a GALAXY_API_KEY secret present in the GitHub 3 | # repository or organization. 4 | # 5 | # See: https://github.com/marketplace/actions/publish-ansible-role-to-galaxy 6 | # See: https://github.com/ansible/galaxy/issues/46 7 | 8 | name: Release 9 | 'on': 10 | push: 11 | tags: 12 | - '*' 13 | 14 | defaults: 15 | run: 16 | working-directory: 'geerlingguy.munin-node' 17 | 18 | jobs: 19 | 20 | release: 21 | name: Release 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Check out the codebase. 25 | uses: actions/checkout@v4 26 | with: 27 | path: 'geerlingguy.munin-node' 28 | 29 | - name: Set up Python 3. 30 | uses: actions/setup-python@v5 31 | with: 32 | python-version: '3.13' # Can't go to 3.14+ until Ansible 13.x 33 | 34 | - name: Install Ansible. 35 | run: pip3 install ansible-core 36 | 37 | - name: Trigger a new import on Galaxy. 38 | run: >- 39 | ansible-galaxy role import --api-key ${{ secrets.GALAXY_API_KEY }} 40 | $(echo ${{ github.repository }} | cut -d/ -f1) $(echo ${{ github.repository }} | cut -d/ -f2) 41 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Close inactive issues 3 | 'on': 4 | schedule: 5 | - cron: "55 21 * * 6" # semi-random time 6 | 7 | jobs: 8 | close-issues: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | pull-requests: write 13 | steps: 14 | - uses: actions/stale@v8 15 | with: 16 | days-before-stale: 120 17 | days-before-close: 60 18 | exempt-issue-labels: bug,pinned,security,planned 19 | exempt-pr-labels: bug,pinned,security,planned 20 | stale-issue-label: "stale" 21 | stale-pr-label: "stale" 22 | stale-issue-message: | 23 | This issue has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution! 24 | 25 | Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale. 26 | close-issue-message: | 27 | This issue has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details. 28 | stale-pr-message: | 29 | This pr has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution! 30 | 31 | Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale. 32 | close-pr-message: | 33 | This pr has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details. 34 | repo-token: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Include OS-specific variables. 3 | include_vars: "{{ ansible_facts.os_family }}.yml" 4 | 5 | - name: Ensure munin-node is installed (RedHat). 6 | yum: name=munin-node state=present enablerepo=epel 7 | when: ansible_facts.os_family == 'RedHat' 8 | 9 | - name: Ensure munin-node is installed (Debian). 10 | apt: name=munin-node state=present 11 | when: ansible_facts.os_family == 'Debian' 12 | 13 | - name: Copy munin-node configuration. 14 | template: 15 | src: munin-node.conf.j2 16 | dest: /etc/munin/munin-node.conf 17 | owner: root 18 | group: root 19 | mode: 0644 20 | notify: restart munin-node 21 | 22 | - name: Generate plugin configuration. 23 | template: 24 | src: plugin-conf.j2 25 | dest: /etc/munin/plugin-conf.d/zzz-ansible.conf 26 | owner: root 27 | group: root 28 | mode: 0644 29 | notify: restart munin-node 30 | 31 | - name: Install extra plugins. 32 | copy: 33 | src: "{{ item.src }}" 34 | dest: "{{ munin_plugin_src_path }}{{ item.src | basename }}" 35 | mode: '0755' 36 | with_items: "{{ munin_node_install_plugins }}" 37 | when: item.src is defined 38 | notify: restart munin-node 39 | 40 | - name: Install extra remote plugins. 41 | get_url: 42 | url: "{{ item.remote_src }}" 43 | dest: "{{ munin_plugin_src_path }}{{ item.remote_src | basename }}" 44 | mode: '0755' 45 | with_items: "{{ munin_node_install_plugins }}" 46 | when: item.remote_src is defined 47 | notify: restart munin-node 48 | 49 | - name: Enable additional plugins. 50 | file: # noqa 208 51 | path: "{{ munin_plugin_dest_path }}{{ item.name }}" 52 | src: "{{ munin_plugin_src_path }}{{ item.plugin | default( item.name ) }}" 53 | state: link 54 | with_items: "{{ munin_node_plugins }}" 55 | notify: restart munin-node 56 | 57 | - name: Ensure munin-node is running. 58 | service: name=munin-node state=started enabled=yes 59 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 'on': 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | schedule: 9 | - cron: "30 2 * * 3" 10 | 11 | defaults: 12 | run: 13 | working-directory: 'geerlingguy.munin-node' 14 | 15 | jobs: 16 | 17 | lint: 18 | name: Lint 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Check out the codebase. 22 | uses: actions/checkout@v4 23 | with: 24 | path: 'geerlingguy.munin-node' 25 | 26 | - name: Set up Python 3. 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: '3.13' # Can't go to 3.14+ until Ansible 13.x 30 | 31 | - name: Install test dependencies. 32 | run: pip3 install yamllint 33 | 34 | - name: Lint code. 35 | run: | 36 | yamllint . 37 | 38 | molecule: 39 | name: Molecule 40 | runs-on: ubuntu-latest 41 | strategy: 42 | matrix: 43 | include: 44 | - distro: rockylinux10 45 | playbook: converge.yml 46 | - distro: ubuntu2404 47 | playbook: converge.yml 48 | - distro: rockylinux10 49 | playbook: playbook-vars.yml 50 | 51 | steps: 52 | - name: Check out the codebase. 53 | uses: actions/checkout@v4 54 | with: 55 | path: 'geerlingguy.munin-node' 56 | 57 | - name: Set up Python 3. 58 | uses: actions/setup-python@v5 59 | with: 60 | python-version: '3.13' # Can't go to 3.14+ until Ansible 13.x 61 | 62 | - name: Install test dependencies. 63 | run: pip3 install ansible molecule molecule-plugins[docker] docker 64 | 65 | - name: Run Molecule tests. 66 | run: molecule test 67 | env: 68 | PY_COLORS: '1' 69 | ANSIBLE_FORCE_COLOR: '1' 70 | MOLECULE_DISTRO: ${{ matrix.distro }} 71 | MOLECULE_PLAYBOOK: ${{ matrix.playbook }} 72 | -------------------------------------------------------------------------------- /templates/munin-node.conf.j2: -------------------------------------------------------------------------------- 1 | # 2 | # Example config-file for munin-node 3 | # 4 | 5 | log_level 4 6 | log_file {{ munin_node_log }} 7 | pid_file {{ munin_node_pid }} 8 | 9 | background 1 10 | setsid 1 11 | 12 | user root 13 | group root 14 | 15 | # This is the timeout for the whole transaction. 16 | # Units are in sec. Default is 15 min 17 | # global_timeout 900 18 | 19 | # This is the timeout for each plugin. 20 | # Units are in sec. Default is 1 min 21 | # timeout 60 22 | 23 | # Regexps for files to ignore 24 | ignore_file [\#~]$ 25 | ignore_file DEADJOE$ 26 | ignore_file \.bak$ 27 | ignore_file %$ 28 | ignore_file \.dpkg-(tmp|new|old|dist)$ 29 | ignore_file \.rpm(save|new)$ 30 | ignore_file \.pod$ 31 | 32 | # Set this if the client doesn't report the correct hostname when 33 | # telnetting to localhost, port 4949 34 | {% if munin_node_host_name %} 35 | host_name {{ munin_node_host_name }} 36 | {% endif %} 37 | 38 | # A list of addresses that are allowed to connect. This must be a 39 | # regular expression, since Net::Server does not understand CIDR-style 40 | # network notation unless the perl module Net::CIDR is installed. You 41 | # may repeat the allow line as many times as you'd like 42 | 43 | {% for allowed_ip in munin_node_allowed_ips %} 44 | allow {{ allowed_ip }} 45 | {% endfor %} 46 | 47 | # If you have installed the Net::CIDR perl module, you can use one or more 48 | # cidr_allow and cidr_deny address/mask patterns. A connecting client must 49 | # match any cidr_allow, and not match any cidr_deny. Note that a netmask 50 | # *must* be provided, even if it's /32 51 | # 52 | # Example: 53 | # 54 | # cidr_allow 127.0.0.1/32 55 | # cidr_allow 192.0.2.0/24 56 | # cidr_deny 192.0.2.42/32 57 | {% for allowed_cidr in munin_node_allowed_cidrs %} 58 | cidr_allow {{ allowed_cidr }} 59 | {% endfor %} 60 | {% for denied_cidr in munin_node_denied_cidrs %} 61 | cidr_deny {{ denied_cidr }} 62 | {% endfor %} 63 | 64 | # Which address to bind to; 65 | host {{ munin_node_bind_host }} 66 | 67 | # And which port 68 | port {{ munin_node_bind_port }} 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible Role: Munin Node 2 | 3 | [![CI](https://github.com/geerlingguy/ansible-role-munin-node/actions/workflows/ci.yml/badge.svg)](https://github.com/geerlingguy/ansible-role-munin-node/actions/workflows/ci.yml) 4 | 5 | Installs munin-node, a monitoring system endpoint, on RedHat/CentOS or Debian/Ubuntu Linux servers. 6 | 7 | ## Requirements 8 | 9 | If using RedHat/CentOS, make sure you have the EPEL repository installed prior to using this role (you can install it using the [`geerlingguy.repo-epel`](https://galaxy.ansible.com/list#/roles/436) role). 10 | 11 | ## Role Variables 12 | 13 | Available variables are listed below, along with default values: 14 | 15 | munin_node_bind_host: "*" 16 | munin_node_bind_port: "4949" 17 | 18 | The host and port to which munin-node will bind. Common host options are `127.0.0.1` (localhost), or `*` (bind to all IP addresses). `4949` is the default Munin port. 19 | 20 | munin_node_host_name: '' 21 | 22 | Set this explicitly if the munin master doesn't report the correct hostname when telnetting in to munin-node. In most cases, the default should work fine. 23 | 24 | munin_node_allowed_ips: 25 | - '^127\.0\.0\.1$' 26 | - '^::1$' 27 | 28 | A list of IP addresses formatted as a python-style regular expression. Must use single quotes to allow the proper regex escaping to pass through to the configuration file. Hosts with these IP addresses will be allowed to connect to the server and get detailed system stats via munin-node. 29 | 30 | munin_node_allowed_cidrs: [] 31 | 32 | A list of IP networks in CIDR format, for instance `10.0.0.0/8`. Hosts with an IP address in one of these networks will be allowed to connect to the server and get detailed system stats via munin-node. 33 | 34 | munin_node_denied_cidrs: [] 35 | 36 | A list of IP networks in CIDR format, for instance `10.42.0.0/16`. Hosts with an IP address in one of these networks will be denied access to the server. This takes precedence over `munin_node_allowed_cidrs`: an IP address that matches both a network in `munin_node_allowed_cidrs` and a network in `munin_node_denied_cidrs` will be denied access. 37 | 38 | ### Munin Plugin Configuration 39 | 40 | You can enable plugins using the `munin_node_plugins` list, like so: 41 | 42 | munin_node_plugins: 43 | - name: uptime 44 | 45 | If the name of the resulting plugin does not match the name of the munin plugin from which it is generated (as is the case, say, with the `if_` plugin), you need to add a `plugin` field to the list item, like so: 46 | 47 | munin_node_plugins: 48 | - name: if_eth0 49 | plugin: if_ 50 | 51 | #### Plugin settings 52 | 53 | If you need to add plugin configuration for plugins you've added via `munin_node_plugins`, you can do so with a simple hashmap that has the plugin name (which will be the `[plugin]` section in the resulting configuration file), and a list of variable names and values. For example: 54 | 55 | munin_node_config: { 56 | "ps_test": { 57 | "env.regex": "bash", 58 | "env.name": "bash" 59 | } 60 | } 61 | 62 | This configuration will generate a configuration file at `/etc/munin/plugin-conf.d/ansible.conf` with the following contents: 63 | 64 | [ps_test] 65 | env.regex bash 66 | env.name bash 67 | 68 | #### Install external plugins 69 | 70 | You can install external plugins via `munin_node_install_plugins`. 71 | Those plugins can be copied from local files or downloaded. For example: 72 | 73 | munin_node_install_plugins: [] 74 | - src: files/munin/redis_ 75 | - remote_src: https://raw.githubusercontent.com/ohitz/phpfpm-multi-munin-plugin/master/phpfpm-multi 76 | 77 | 78 | ## Dependencies 79 | 80 | None. 81 | 82 | ## Example Playbook 83 | 84 | - hosts: servers 85 | roles: 86 | - { role: geerlingguy.munin-node } 87 | 88 | ## License 89 | 90 | MIT / BSD 91 | 92 | ## Author Information 93 | 94 | This role was created in 2014 by [Jeff Geerling](https://www.jeffgeerling.com/), author of [Ansible for DevOps](https://www.ansiblefordevops.com/). 95 | 96 | Munin plugin configuration was added by Rafał Trójniak . 97 | --------------------------------------------------------------------------------