forked from evolix/ansible-roles
Merge branch 'unstable' into stable
This commit is contained in:
commit
d7d58bf158
27
CHANGELOG.md
27
CHANGELOG.md
|
@ -20,16 +20,41 @@ The **patch** part changes is incremented if multiple releases happen the same m
|
|||
|
||||
### Security
|
||||
|
||||
## [22.03] 2022-03-02
|
||||
|
||||
### Added
|
||||
|
||||
* apt: apt_hold_packages: broadcast message with wall, if present
|
||||
* evolinux-base: option to bypass raid-related tasks
|
||||
* Explicit permissions for systemd overrides
|
||||
* generate-ldif: Add support for php-fpm in containers
|
||||
* kvm-host: add missing default value
|
||||
* lxc-php: preliminary support for PHP 8.1 container
|
||||
* openvpn: now check that openvpn has been restarted since last certificates renewal
|
||||
* redis: always install check_redis_instances
|
||||
* redis: check_redis_instances tolerates absence of instances
|
||||
|
||||
### Changed
|
||||
|
||||
* elasticsearch: Use `/etc/elasticsearch/jvm.options.d/evolinux` instead of default `/etc/elasticsearch/jvm.options`
|
||||
* evolinux-users: check permissions for /etc/sudoers.d
|
||||
* evolinux-users: optimize sudo configuration
|
||||
* lxc: Fail if /var is nosuid
|
||||
* openvpn: make it compatible with OpenBSD and add some improvements
|
||||
|
||||
|
||||
|
||||
## [22.01.3] 2022-01-31
|
||||
|
||||
### Changed
|
||||
|
||||
* rbenv: install Ruby 3.1.0 by default
|
||||
* evolinux-base: backup-server-state: add "force" mode
|
||||
|
||||
### Fixed
|
||||
|
||||
* evolinux-base: backup-server-state: fix systemctl invocation
|
||||
|
||||
* varnish: update munin plugin to work with recent varnish versions
|
||||
|
||||
## [22.01.2] 2022-01-27
|
||||
|
||||
|
|
|
@ -21,7 +21,12 @@ if [ -f ${config_file} ]; then
|
|||
if [ -n "${package}" ]; then
|
||||
if is_installed ${package} && ! is_held ${package}; then
|
||||
apt-mark hold ${package}
|
||||
>&2 echo "Package \`${package}' has been marked \`hold'."
|
||||
msg="Package \`${package}' has been marked \`hold'."
|
||||
>&2 echo "${msg}"
|
||||
wall_bin=$(command -v wall)
|
||||
if [ -n "${wall_bin}" ]; then
|
||||
"${wall_bin}" --timeout 5 "${msg}"
|
||||
fi
|
||||
return_code=1
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -69,6 +69,15 @@
|
|||
tags:
|
||||
- dovecot
|
||||
|
||||
- name: deploy file for custom configuration
|
||||
template:
|
||||
src: zzz-evolinux-custom.conf.j2
|
||||
dest: /etc/dovecot/conf.d/zzz-evolinux-custom.conf
|
||||
mode: "0644"
|
||||
notify: reload dovecot
|
||||
tags:
|
||||
- dovecot
|
||||
|
||||
- include: munin.yml
|
||||
tags:
|
||||
- dovecot
|
||||
|
|
1
dovecot/templates/zzz-evolinux-custom.conf.j2
Normal file
1
dovecot/templates/zzz-evolinux-custom.conf.j2
Normal file
|
@ -0,0 +1 @@
|
|||
## Put your customized configuration here, verify configuration with "doveconf -n" and /var/log/mail.log
|
|
@ -96,17 +96,25 @@
|
|||
|
||||
- name: JVM Heap size (min) is set
|
||||
lineinfile:
|
||||
dest: /etc/elasticsearch/jvm.options
|
||||
dest: /etc/elasticsearch/jvm.options.d/evolinux.options
|
||||
regexp: "^-Xms"
|
||||
line: "-Xms{{ elasticsearch_jvm_xms }}"
|
||||
create: yes
|
||||
owner: root
|
||||
group: elasticsearch
|
||||
mode: 0640
|
||||
tags:
|
||||
- config
|
||||
|
||||
- name: JVM Heap size (max) is set
|
||||
lineinfile:
|
||||
dest: /etc/elasticsearch/jvm.options
|
||||
dest: /etc/elasticsearch/jvm.options.d/evolinux.options
|
||||
regexp: "^-Xmx"
|
||||
line: "-Xmx{{ elasticsearch_jvm_xmx }}"
|
||||
create: yes
|
||||
owner: root
|
||||
group: elasticsearch
|
||||
mode: 0640
|
||||
tags:
|
||||
- config
|
||||
|
||||
|
|
|
@ -26,10 +26,13 @@
|
|||
|
||||
- name: change JVM tmpdir (< 6.x)
|
||||
lineinfile:
|
||||
dest: /etc/elasticsearch/jvm.options
|
||||
dest: /etc/elasticsearch/jvm.options.d/evolinux.options
|
||||
line: "-Djava.io.tmpdir={{ _elasticsearch_custom_tmpdir }}"
|
||||
regexp: "^-Djava.io.tmpdir="
|
||||
insertafter: "## JVM configuration"
|
||||
create: yes
|
||||
owner: root
|
||||
group: elasticsearch
|
||||
mode: 0640
|
||||
notify:
|
||||
- restart elasticsearch
|
||||
tags:
|
||||
|
@ -48,6 +51,7 @@
|
|||
- elasticsearch
|
||||
when: elastic_stack_version is version('6', '>=')
|
||||
|
||||
# Note : Should not do any changes as -Djava.io.tmpdir=${ES_TMPDIR} is already here in the default config.
|
||||
- name: change JVM tmpdir (>= 6.x)
|
||||
lineinfile:
|
||||
dest: /etc/elasticsearch/jvm.options
|
||||
|
|
|
@ -78,6 +78,7 @@ evolinux_packages_include: True
|
|||
evolinux_packages_system: True
|
||||
evolinux_packages_diagnostic: True
|
||||
evolinux_packages_hardware: True
|
||||
evolinux_packages_hardware_raid: True
|
||||
evolinux_packages_common: True
|
||||
evolinux_packages_stretch: True
|
||||
evolinux_packages_buster: True
|
||||
|
|
|
@ -101,6 +101,7 @@
|
|||
when:
|
||||
- "'Hewlett-Packard Company Smart Array' in raidmodel.stdout"
|
||||
- "'Adaptec Smart Storage PQI' in raidmodel.stdout"
|
||||
- evolinux_packages_hardware_raid | bool
|
||||
|
||||
# NOTE: check_hpraid cron use check_hpraid from nagios-nrpe role
|
||||
# So, if nagios-nrpe role is not installed it will not work
|
||||
|
@ -149,9 +150,11 @@
|
|||
tags:
|
||||
- packages
|
||||
- config
|
||||
when: "'Hewlett-Packard Company Smart Array' in raidmodel.stdout"
|
||||
when:
|
||||
- "'Hewlett-Packard Company Smart Array' in raidmodel.stdout"
|
||||
- evolinux_packages_hardware_raid | bool
|
||||
|
||||
- name: MegaRAID SAS package is present
|
||||
- name: MegaCLI SAS package is present
|
||||
block:
|
||||
- name: HWRaid embedded GPG key is absent
|
||||
apt_key:
|
||||
|
@ -206,6 +209,8 @@
|
|||
tags:
|
||||
- packages
|
||||
- config
|
||||
when: "'MegaRAID' in raidmodel.stdout"
|
||||
when:
|
||||
- "'MegaRAID' in raidmodel.stdout"
|
||||
- evolinux_packages_hardware_raid | bool
|
||||
|
||||
- meta: flush_handlers
|
||||
|
|
|
@ -20,10 +20,6 @@
|
|||
|
||||
- name: Configure sudo
|
||||
include: sudo.yml
|
||||
vars:
|
||||
user: "{{ item.value }}"
|
||||
loop: "{{ evolinux_users | dict2items }}"
|
||||
when: evolinux_users | length > 0
|
||||
|
||||
- name: Configure SSH
|
||||
include: ssh.yml
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
---
|
||||
|
||||
- include: sudo_jessie.yml
|
||||
when: ansible_distribution_release == "jessie"
|
||||
vars:
|
||||
user: "{{ item.value }}"
|
||||
loop: "{{ evolinux_users | dict2items }}"
|
||||
when:
|
||||
- evolinux_users | length > 0
|
||||
- ansible_distribution_release == "jessie"
|
||||
|
||||
- include: sudo_stretch.yml
|
||||
|
||||
- block:
|
||||
- include: sudo_stretch_common.yml
|
||||
|
||||
- include: sudo_stretch_user.yml
|
||||
vars:
|
||||
user: "{{ item.value }}"
|
||||
loop: "{{ evolinux_users | dict2items }}"
|
||||
when:
|
||||
- ansible_distribution_major_version is defined
|
||||
- ansible_distribution_major_version is version('9', '>=')
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
---
|
||||
|
||||
- name: "/etc/sudoers.d presence and permissions"
|
||||
file:
|
||||
path: /etc/sudoers.d
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
state: directory
|
||||
|
||||
- name: "Verify 'evolinux' sudoers file presence (Debian 9 or later)"
|
||||
template:
|
||||
src: sudoers_stretch.j2
|
||||
|
@ -13,15 +21,3 @@
|
|||
group:
|
||||
name: "{{ evolinux_sudo_group }}"
|
||||
system: yes
|
||||
|
||||
- name: "Add user to '{{ evolinux_sudo_group }}' group (Debian 9 or later)"
|
||||
user:
|
||||
name: '{{ user.name }}'
|
||||
groups: "{{ evolinux_sudo_group }}"
|
||||
append: yes
|
||||
|
||||
- name: "Add user to 'adm' group (Debian 9 or later)"
|
||||
user:
|
||||
name: '{{ user.name }}'
|
||||
groups: "adm"
|
||||
append: yes
|
13
evolinux-users/tasks/sudo_stretch_user.yml
Normal file
13
evolinux-users/tasks/sudo_stretch_user.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
|
||||
- name: "Add user to '{{ evolinux_sudo_group }}' group (Debian 9 or later)"
|
||||
user:
|
||||
name: '{{ user.name }}'
|
||||
groups: "{{ evolinux_sudo_group }}"
|
||||
append: yes
|
||||
|
||||
- name: "Add user to 'adm' group (Debian 9 or later)"
|
||||
user:
|
||||
name: '{{ user.name }}'
|
||||
groups: "adm"
|
||||
append: yes
|
|
@ -602,6 +602,96 @@ ServiceVersion: PostgreSQL ${postgresql_version}
|
|||
EOT
|
||||
fi
|
||||
|
||||
# LXC (multiphp)
|
||||
if is_pkg_installed lxc; then
|
||||
|
||||
if lxc-ls | grep -q php56 ; then
|
||||
cat <<EOT >> "${ldif_file}"
|
||||
|
||||
dn: ServiceName=ServiceName=php-fpm56,${computer_dn}
|
||||
NagiosEnabled: TRUE
|
||||
ipServiceProtocol: tcp
|
||||
objectClass: EvoService
|
||||
ServiceName: PHP-FPM (multiphp)
|
||||
ipServicePort: 443
|
||||
ServiceType: web
|
||||
ServiceVersion: PHP-FPM 5.6 (multiphp)
|
||||
EOT
|
||||
fi
|
||||
|
||||
if lxc-ls | grep -q php70 ; then
|
||||
cat <<EOT >> "${ldif_file}"
|
||||
|
||||
dn: ServiceName=ServiceName=php-fpm70,${computer_dn}
|
||||
NagiosEnabled: TRUE
|
||||
ipServiceProtocol: tcp
|
||||
objectClass: EvoService
|
||||
ServiceName: PHP-FPM (multiphp)
|
||||
ipServicePort: 443
|
||||
ServiceType: web
|
||||
ServiceVersion: PHP-FPM 7.0 (multiphp)
|
||||
EOT
|
||||
fi
|
||||
|
||||
if lxc-ls | grep -q php73 ; then
|
||||
cat <<EOT >> "${ldif_file}"
|
||||
|
||||
dn: ServiceName=ServiceName=php-fpm73,${computer_dn}
|
||||
NagiosEnabled: TRUE
|
||||
ipServiceProtocol: tcp
|
||||
objectClass: EvoService
|
||||
ServiceName: PHP-FPM (multiphp)
|
||||
ipServicePort: 443
|
||||
ServiceType: web
|
||||
ServiceVersion: PHP-FPM 7.3 (multiphp)
|
||||
EOT
|
||||
fi
|
||||
|
||||
if lxc-ls | grep -q php74 ; then
|
||||
cat <<EOT >> "${ldif_file}"
|
||||
|
||||
dn: ServiceName=ServiceName=php-fpm74,${computer_dn}
|
||||
NagiosEnabled: TRUE
|
||||
ipServiceProtocol: tcp
|
||||
objectClass: EvoService
|
||||
ServiceName: PHP-FPM (multiphp)
|
||||
ipServicePort: 443
|
||||
ServiceType: web
|
||||
ServiceVersion: PHP-FPM 7.4 (multiphp)
|
||||
EOT
|
||||
fi
|
||||
|
||||
if lxc-ls | grep -q php80 ; then
|
||||
cat <<EOT >> "${ldif_file}"
|
||||
|
||||
dn: ServiceName=ServiceName=php-fpm80,${computer_dn}
|
||||
NagiosEnabled: TRUE
|
||||
ipServiceProtocol: tcp
|
||||
objectClass: EvoService
|
||||
ServiceName: PHP-FPM (multiphp)
|
||||
ipServicePort: 443
|
||||
ServiceType: web
|
||||
ServiceVersion: PHP-FPM 8.0 (multiphp)
|
||||
EOT
|
||||
fi
|
||||
|
||||
if lxc-ls | grep -q php81 ; then
|
||||
cat <<EOT >> "${ldif_file}"
|
||||
|
||||
dn: ServiceName=ServiceName=php-fpm81,${computer_dn}
|
||||
NagiosEnabled: TRUE
|
||||
ipServiceProtocol: tcp
|
||||
objectClass: EvoService
|
||||
ServiceName: PHP-FPM (multiphp)
|
||||
ipServicePort: 443
|
||||
ServiceType: web
|
||||
ServiceVersion: PHP-FPM 8.1 (multiphp)
|
||||
EOT
|
||||
fi
|
||||
|
||||
fi
|
||||
# END - LXC (multiphp)
|
||||
|
||||
# mdadm
|
||||
if is_pkg_installed mdadm; then
|
||||
mdadm_version=$(get_pkg_version mdadm)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
---
|
||||
kvm_custom_libvirt_images_path: ''
|
||||
kvm_install_drbd: True
|
||||
kvm_scripts_dir: /usr/local/sbin
|
||||
kvm_scripts_dir: /usr/local/sbin
|
||||
|
||||
kvm_pair: null
|
|
@ -34,7 +34,11 @@
|
|||
special_time: "hourly"
|
||||
user: root
|
||||
job: "rsync -a --delete /etc/libvirt/qemu/*xml {{ hostvars[kvm_pair]['lan.ip'] }}:/root/libvirt-{{ inventory_hostname }}/"
|
||||
when: kvm_pair != inventory_hostname
|
||||
when:
|
||||
- kvm_pair is defined
|
||||
- kvm_pair is not none
|
||||
- kvm_pair | length > 0
|
||||
- kvm_pair != inventory_hostname
|
||||
tags: crontab
|
||||
|
||||
- name: Crontab for sync list of running vm
|
||||
|
@ -44,5 +48,9 @@
|
|||
special_time: "daily"
|
||||
user: root
|
||||
job: "virsh list --all | tee /root/virsh-list.txt | ssh {{ hostvars[kvm_pair]['lan.ip'] }} 'cat >/root/libvirt-{{ inventory_hostname }}/virsh-list.txt'"
|
||||
when: kvm_pair != inventory_hostname
|
||||
when:
|
||||
- kvm_pair is defined
|
||||
- kvm_pair is not none
|
||||
- kvm_pair | length > 0
|
||||
- kvm_pair != inventory_hostname
|
||||
tags: crontab
|
||||
|
|
|
@ -20,3 +20,4 @@ lxc_php_container_releases:
|
|||
php73: "buster"
|
||||
php74: "bullseye"
|
||||
php80: "bullseye"
|
||||
php81: "bullseye"
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
---
|
||||
- name: Reload php81-fpm
|
||||
lxc_container:
|
||||
name: "{{ lxc_php_version }}"
|
||||
container_command: "systemctl reload php8.1-fpm"
|
||||
|
||||
- name: Reload php80-fpm
|
||||
lxc_container:
|
||||
name: "{{ lxc_php_version }}"
|
||||
|
|
|
@ -24,4 +24,7 @@
|
|||
- include: "php80.yml"
|
||||
when: lxc_php_version == "php80"
|
||||
|
||||
- include: "php81.yml"
|
||||
when: lxc_php_version == "php81"
|
||||
|
||||
- include: "misc.yml"
|
||||
|
|
63
lxc-php/tasks/php81.yml
Normal file
63
lxc-php/tasks/php81.yml
Normal file
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
|
||||
- name: "{{ lxc_php_version }} - Install dependency packages"
|
||||
lxc_container:
|
||||
name: "{{ lxc_php_version }}"
|
||||
container_command: "DEBIAN_FRONTEND=noninteractive apt install -y wget apt-transport-https gnupg"
|
||||
|
||||
- name: "{{ lxc_php_version }} - fix bullseye repository"
|
||||
replace:
|
||||
dest: "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/sources.list"
|
||||
regexp: 'bullseye/updates'
|
||||
replace: 'bullseye-security'
|
||||
|
||||
- name: "{{ lxc_php_version }} - Add sury repo"
|
||||
lineinfile:
|
||||
dest: "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/sources.list.d/sury.list"
|
||||
line: "{{ item }}"
|
||||
state: present
|
||||
create: yes
|
||||
mode: "0644"
|
||||
loop:
|
||||
- "deb https://packages.sury.org/php/ bullseye main"
|
||||
- "deb http://pub.evolix.net/ bullseye-php81/"
|
||||
|
||||
- name: copy pub.evolix.net GPG key
|
||||
copy:
|
||||
src: reg.asc
|
||||
dest: /var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/trusted.gpg.d/reg.asc
|
||||
mode: "0644"
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: copy packages.sury.org GPG Key
|
||||
copy:
|
||||
src: sury.gpg
|
||||
dest: /var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/apt/trusted.gpg.d/sury.gpg
|
||||
mode: "0644"
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: "{{ lxc_php_version }} - Update APT cache"
|
||||
lxc_container:
|
||||
name: "{{ lxc_php_version }}"
|
||||
container_command: "DEBIAN_FRONTEND=noninteractive apt update"
|
||||
|
||||
- name: "{{ lxc_php_version }} - Install PHP packages"
|
||||
lxc_container:
|
||||
name: "{{ lxc_php_version }}"
|
||||
container_command: "DEBIAN_FRONTEND=noninteractive apt install -y php-fpm php-cli php-gd php-intl php-imap php-ldap php-mysql php-pgsql php-sqlite3 php-curl php-zip php-mbstring php-zip composer libphp-phpmailer"
|
||||
|
||||
- name: "{{ lxc_php_version }} - Copy evolinux PHP configuration"
|
||||
template:
|
||||
src: z-evolinux-defaults.ini.j2
|
||||
dest: "{{ line_item }}"
|
||||
mode: "0644"
|
||||
notify: "Reload {{ lxc_php_version }}-fpm"
|
||||
loop:
|
||||
- "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/php/8.1/fpm/conf.d/z-evolinux-defaults.ini"
|
||||
- "/var/lib/lxc/{{ lxc_php_version }}/rootfs/etc/php/8.1/cli/conf.d/z-evolinux-defaults.ini"
|
||||
loop_control:
|
||||
loop_var: line_item
|
||||
|
||||
- include: "mail_opensmtpd.yml"
|
|
@ -43,8 +43,8 @@
|
|||
- lxc_unprivilegied_containers | bool
|
||||
- root_subuids.rc != 0
|
||||
|
||||
- name: Check if /var has not mount options nodev or noexec
|
||||
shell: findmnt | grep -E "/var[^/]" | grep -e nodev -e noexec
|
||||
- name: Check if /var has not mount options or nosuid or nodev or noexec
|
||||
shell: findmnt | grep -E "/var[^/]" | grep -e nodev -e noexec -e nosuid
|
||||
register: check_var
|
||||
changed_when: false
|
||||
failed_when: "check_var.rc == 0"
|
||||
|
|
|
@ -32,3 +32,8 @@ lxc.arch = x86_64
|
|||
|
||||
# Start containers on boot by default
|
||||
lxc.start.auto = 1
|
||||
|
||||
{% if ansible_distribution_major_version is version('9', '>') %}
|
||||
# Set LXC container unconfined in AppArmor
|
||||
lxc.apparmor.profile = unconfined
|
||||
{% endif %}
|
||||
|
|
|
@ -102,6 +102,8 @@
|
|||
option: "ProtectHome"
|
||||
value: "false"
|
||||
state: present
|
||||
create: yes
|
||||
mode: "0644"
|
||||
notify:
|
||||
- systemd daemon-reload
|
||||
- restart munin-node
|
||||
|
|
30
nagios-nrpe/files/plugins/check_mount_rw
Executable file
30
nagios-nrpe/files/plugins/check_mount_rw
Executable file
|
@ -0,0 +1,30 @@
|
|||
#!/bin/sh
|
||||
|
||||
output=$(mktemp --tmpdir $(basename $0).XXXXXXXXXX)
|
||||
critical_count=0
|
||||
ok_count=0
|
||||
|
||||
trap "rm -f $output" EXIT
|
||||
|
||||
for mountpoint in $@; do
|
||||
if findmnt -O ro --noheadings "$mountpoint" 1>/dev/null 2>&1; then
|
||||
echo "CRITICAL - $mountpoint" >> "$output"
|
||||
critical_count=$(( critical_count + 1))
|
||||
else
|
||||
echo "OK - $mountpoint" >> "$output"
|
||||
ok_count=$(( ok_count + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
total_count=$(( ok_count + critical_count ))
|
||||
|
||||
if [ $ok_count -eq $total_count ]; then
|
||||
printf "OK - %d/%d no read-only mountpoint\n\n" "$ok_count" "$total_count"
|
||||
cat "$output"
|
||||
exit 0
|
||||
else
|
||||
printf "CRITICAL - %d/%d read-only mountpoint\n\n" "$critical_count" "$total_count"
|
||||
cat "$output"
|
||||
exit 2
|
||||
fi
|
||||
|
|
@ -80,6 +80,7 @@ command[check_php-fpm70]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi
|
|||
command[check_php-fpm73]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php73/rootfs/etc/php/7.3/fpm/pool.d/
|
||||
command[check_php-fpm74]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php74/rootfs/etc/php/7.4/fpm/pool.d/
|
||||
command[check_php-fpm80]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php80/rootfs/etc/php/8.0/fpm/pool.d/
|
||||
command[check_php-fpm81]=sudo {{ nagios_plugins_directory }}/check_phpfpm_multi /var/lib/lxc/php81/rootfs/etc/php/8.1/fpm/pool.d/
|
||||
command[check_ipmi_sensors]=sudo /usr/lib/nagios/plugins/check_ipmi_sensor
|
||||
command[check_raid_status]=/usr/lib/nagios/plugins/check_raid
|
||||
|
||||
|
|
|
@ -66,6 +66,20 @@
|
|||
- name: Include IP address whitelist task
|
||||
include: ip_whitelist.yml
|
||||
|
||||
- name: Copy evolinux_server_custom
|
||||
copy:
|
||||
src: nginx/snippets/evolinux_server_custom
|
||||
dest: /etc/nginx/snippets/evolinux_server_custom
|
||||
owner: www-data
|
||||
group: www-data
|
||||
directory_mode: "0640"
|
||||
mode: "0640"
|
||||
force: no
|
||||
notify: reload nginx
|
||||
tags:
|
||||
- nginx
|
||||
- ips
|
||||
|
||||
- name: Copy private_htpasswd
|
||||
copy:
|
||||
src: nginx/snippets/private_htpasswd
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
UserID opendkim:opendkim
|
||||
Socket inet:54321@127.0.0.1
|
||||
Socket inet:8891@127.0.0.1
|
||||
PidFile /var/run/opendkim/opendkim.pid
|
||||
OversignHeaders From
|
||||
TrustAnchorFile /usr/share/dns/root.key
|
||||
|
|
|
@ -7,6 +7,16 @@ Install and configure OpenVPN, based on [our HowtoOpenVPN wiki](https://wiki.evo
|
|||
Everything is in the `tasks/main.yml` file.
|
||||
Some manual actions are requested at the end of the playbook, to do before finishing the playbook.
|
||||
|
||||
Here is a copy of what is requested :
|
||||
|
||||
* You have to manually create the CA on the server with `shellpki init server.example.com`. The command will ask you to create a password, and will ask you again to give the same one several times.
|
||||
* You have to manually generate the CRL on the server with `openssl ca -gencrl -keyfile /etc/shellpki/cakey.key -cert /etc/shellpki/cacert.pem -out /etc/shellpki/crl.pem -config /etc/shellpki/openssl.cnf`. The previously created password will be asked.
|
||||
* You have to manually create the server's certificate with `shellpki create server.example.com`.
|
||||
* You have to adjust the config file `/etc/openvpn/server.conf` for the following parameters : `local` (to check), `cert` (to check), `key` (to add), `server` (to check), `push` (to complete if needed).
|
||||
* Finally, you can (re)start the OpenVPN service with `systemctl restart openvpn@server.service` on Debian, or `rcctl restart openvpn` on OpenBSD.
|
||||
|
||||
Then, you can use `shellpki` to generate client certificates.
|
||||
|
||||
## Variables
|
||||
|
||||
* `openvpn_lan`: network to use for OpenVPN
|
||||
|
@ -15,5 +25,4 @@ Some manual actions are requested at the end of the playbook, to do before finis
|
|||
|
||||
## TODO
|
||||
|
||||
* Make it compatible with OpenBSD
|
||||
* See TODO tasks in tasks/main.yml
|
||||
* See TODO tasks in tasks/*.yml
|
||||
|
|
|
@ -16,13 +16,22 @@ CA_ECHO=""
|
|||
|
||||
error() {
|
||||
if [ $? -eq 2 ] && [ "X$CERT_ECHO" = "X" ] && [ "X$CA_ECHO" = "X" ] ; then
|
||||
echo "CRITICAL - The check exited with an error. Is the conf_file var containing the real conf file location ? On Debian, is the check executed with sudo ?"
|
||||
echo "CRITICAL - The check exited with an error. Is the conf_file var containing the real conf file location ? On Debian, is the check executed with sudo ? On OpenBSD, is the check executed with doas ? Is OpenVPN running ?"
|
||||
fi
|
||||
}
|
||||
|
||||
SYSTEM=$(uname | tr '[:upper:]' '[:lower:]')
|
||||
date_cmd=$(command -v date)
|
||||
|
||||
# Some backup servers don't have OpenVPN running while they are backup
|
||||
is_backup_not_running_openvpn="1"
|
||||
if [ "$SYSTEM" = "openbsd" ]; then
|
||||
carp=$(/sbin/ifconfig carp0 2>/dev/null | grep 'status' | cut -d' ' -f2)
|
||||
if [ "$carp" = "backup" ] && ! rcctl ls on | grep -q openvpn; then
|
||||
is_backup_not_running_openvpn="0"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Dates in seconds
|
||||
_15_days="1296000"
|
||||
_30_days="2592000"
|
||||
|
@ -30,7 +39,7 @@ current_date=$($date_cmd +"%s")
|
|||
|
||||
# Trying to define the OpenVPN conf file location - default to /etc/openvpn/server.conf
|
||||
conf_file=$(ps auwwwx | grep openvpn | grep -- --config | grep -v sed | sed -e "s/.*config \(\/etc\/openvpn.*.conf\).*/\1/" | head -1)
|
||||
[ "$SYSTEM" = "openbsd" ] && conf_file=${conf_file:-$(grep openvpn_flags /etc/rc.conf.local | sed -e "s/.*config \(\/etc\/openvpn.*.conf\).*/\1/")}
|
||||
if [ "$SYSTEM" = "openbsd" ]; then conf_file=${conf_file:-$(grep openvpn_flags /etc/rc.conf.local | sed -e "s/.*config \(\/etc\/openvpn.*.conf\).*/\1/")}; fi
|
||||
conf_file=${conf_file:-"/etc/openvpn/server.conf"}
|
||||
|
||||
# Get the cert and ca file location, based on the OpenVPN conf file location
|
||||
|
@ -44,22 +53,34 @@ ca_file=$(echo $ca_file | sed -e "s/^ca *\//\//")
|
|||
cert_expiration_date=$(grep "Not After" $cert_file | sed -e "s/.*Not After : //")
|
||||
ca_expiration_date=$(openssl x509 -enddate -noout -in $ca_file | cut -d '=' -f 2)
|
||||
|
||||
# Get the date of last modification of cert and ca certificates
|
||||
if [ "$SYSTEM" = "openbsd" ]; then
|
||||
seconds_last_cert_modification_date=$(stat -f %m "$cert_file")
|
||||
seconds_last_ca_modification_date=$(stat -f %m "$ca_file")
|
||||
else
|
||||
seconds_last_cert_modification_date=$(stat -c %Y "$cert_file")
|
||||
seconds_last_ca_modification_date=$(stat -c %Y "$ca_file")
|
||||
fi
|
||||
|
||||
# Get the date of last OpenVPN restart
|
||||
last_openvpn_restart_date=$(ps awwwx -O lstart | grep openvpn | grep -- --daemon | grep -- --config | head -1 | awk '{print $3,$4,$5,$6}')
|
||||
|
||||
test_cert_expiration() {
|
||||
# Already expired - Cert file
|
||||
if [ $current_date -ge $1 ]; then
|
||||
CERT_ECHO="CRITICAL - The server certificate has expired on $formatted_cert_expiration_date"
|
||||
CERT_ECHO="CRITICAL - The server certificate has expired on $formated_cert_expiration_date"
|
||||
CERT_STATE=$STATE_CRITICAL
|
||||
# Expiration in 15 days or less - Cert file
|
||||
elif [ $((current_date+_15_days)) -ge $1 ]; then
|
||||
CERT_ECHO="CRITICAL - The server certificate expires in 15 days or less : $formatted_cert_expiration_date"
|
||||
CERT_ECHO="CRITICAL - The server certificate expires in 15 days or less : $formated_cert_expiration_date"
|
||||
CERT_STATE=$STATE_CRITICAL
|
||||
# Expiration in 30 days or less - Cert file
|
||||
elif [ $((current_date+_30_days)) -ge $1 ]; then
|
||||
CERT_ECHO="WARNING - The server certificate expires in 30 days or less : $formatted_cert_expiration_date"
|
||||
CERT_ECHO="WARNING - The server certificate expires in 30 days or less : $formated_cert_expiration_date"
|
||||
CERT_STATE=$STATE_WARNING
|
||||
# Expiration in more than 30 days - Cert file
|
||||
else
|
||||
CERT_ECHO="OK - The server certificate expires on $formatted_cert_expiration_date"
|
||||
CERT_ECHO="OK - The server certificate expires on $formated_cert_expiration_date"
|
||||
CERT_STATE=$STATE_OK
|
||||
fi
|
||||
}
|
||||
|
@ -67,74 +88,117 @@ test_cert_expiration() {
|
|||
test_ca_expiration() {
|
||||
# Already expired - CA file
|
||||
if [ $current_date -ge $1 ]; then
|
||||
CA_ECHO="CRITICAL - The server CA has expired on $formatted_ca_expiration_date"
|
||||
CA_ECHO="CRITICAL - The server CA has expired on $formated_ca_expiration_date"
|
||||
CA_STATE=$STATE_CRITICAL
|
||||
# Expiration in 15 days or less - CA file
|
||||
elif [ $((current_date+_15_days)) -ge $1 ]; then
|
||||
CA_ECHO="CRITICAL - The server CA expires in 15 days or less : $formatted_ca_expiration_date"
|
||||
CA_ECHO="CRITICAL - The server CA expires in 15 days or less : $formated_ca_expiration_date"
|
||||
CA_STATE=$STATE_CRITICAL
|
||||
# Expiration in 30 days or less - CA file
|
||||
elif [ $((current_date+_30_days)) -ge $1 ]; then
|
||||
CA_ECHO="WARNING - The server CA expires in 30 days or less : $formatted_ca_expiration_date"
|
||||
CA_ECHO="WARNING - The server CA expires in 30 days or less : $formated_ca_expiration_date"
|
||||
CA_STATE=$STATE_WARNING
|
||||
# Expiration in more than 30 days - CA file
|
||||
else
|
||||
CA_ECHO="OK - The server CA expires on $formatted_ca_expiration_date"
|
||||
CA_ECHO="OK - The server CA expires on $formated_ca_expiration_date"
|
||||
CA_STATE=$STATE_OK
|
||||
fi
|
||||
}
|
||||
|
||||
# Linux and BSD systems do not implement 'date' the same way
|
||||
if [ "$SYSTEM" = "linux" ]; then
|
||||
test_openvpn_restarted_since_last_ca_cert_modification() {
|
||||
if [ $is_backup_not_running_openvpn -eq "0" ]; then
|
||||
RESTART_ECHO="OK - OpenVPN is not running because server is backup"
|
||||
RESTART_STATE=$STATE_OK
|
||||
else
|
||||
if [ $seconds_last_cert_modification_date -ge $1 ] || [ $seconds_last_ca_modification_date -ge $1 ]; then
|
||||
RESTART_ECHO="CRITICAL - OpenVPN hasn't been restarted since the last renewal of CA or CERT certificate"
|
||||
RESTART_STATE=$STATE_CRITICAL
|
||||
else
|
||||
RESTART_ECHO="OK - OpenVPN has been restarted since the last renewal of CA and CERT certificate"
|
||||
RESTART_STATE=$STATE_OK
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Cert expiration date human formated then in seconds
|
||||
formatted_cert_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$cert_expiration_date" +"%F %T %Z")
|
||||
seconds_cert_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$cert_expiration_date" +"%s")
|
||||
main() {
|
||||
# Linux and BSD systems do not implement 'date' the same way
|
||||
if [ "$SYSTEM" = "linux" ]; then
|
||||
|
||||
# Cert expiration date human formated then in seconds
|
||||
formated_cert_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$cert_expiration_date" +"%F %T %Z")
|
||||
seconds_cert_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$cert_expiration_date" +"%s")
|
||||
|
||||
# CA expiration date human formated then in seconds
|
||||
formated_ca_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$ca_expiration_date" +"%F %T %Z")
|
||||
seconds_ca_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$ca_expiration_date" +"%s")
|
||||
|
||||
# CA expiration date human formated then in seconds
|
||||
formatted_ca_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$ca_expiration_date" +"%F %T %Z")
|
||||
seconds_ca_expiration_date=$(TZ="Europe/Paris" $date_cmd -d "$ca_expiration_date" +"%s")
|
||||
# Last OpenVPN restart in seconds
|
||||
seconds_last_openvpn_restart_date=$(TZ="Europe/Paris" $date_cmd -d "$last_openvpn_restart_date" +%s)
|
||||
|
||||
test_cert_expiration $seconds_cert_expiration_date
|
||||
test_ca_expiration $seconds_ca_expiration_date
|
||||
test_openvpn_restarted_since_last_ca_cert_modification $seconds_last_openvpn_restart_date
|
||||
|
||||
elif [ "$SYSTEM" = "openbsd" ]; then
|
||||
|
||||
test_cert_expiration $seconds_cert_expiration_date
|
||||
test_ca_expiration $seconds_ca_expiration_date
|
||||
# Cert expiration date for POSIX date, human formated then in seconds
|
||||
posix_cert_expiration_date=$(echo "$cert_expiration_date" | awk '{ printf $4" "(index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3" "$2" ",split($3,time,":"); print time[1],time[2],time[3]}' | awk '{printf "%04d%02d%02d%02d%02d.%02d\n", $1, $2, $3, $4, $5, $6}')
|
||||
cert_zone=$(echo "$cert_expiration_date" | awk '{print $5}')
|
||||
formated_cert_expiration_date=$(TZ=$cert_zone $date_cmd -j -z "Europe/Paris" "$posix_cert_expiration_date" +"%F %T %Z")
|
||||
seconds_cert_expiration_date=$(TZ=$cert_zone $date_cmd -j -z "Europe/Paris" "$posix_cert_expiration_date" +"%s")
|
||||
|
||||
# CA expiration date for POSIX date, human formated then in seconds
|
||||
posix_ca_expiration_date=$(echo "$ca_expiration_date" | awk '{ printf $4" "(index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3" "$2" ",split($3,time,":"); print time[1],time[2],time[3]}' | awk '{printf "%04d%02d%02d%02d%02d.%02d\n", $1, $2, $3, $4, $5, $6}')
|
||||
ca_zone=$(echo "$ca_expiration_date" | awk '{print $5}')
|
||||
formated_ca_expiration_date=$(TZ=$ca_zone $date_cmd -j -z "Europe/Paris" "$posix_ca_expiration_date" +"%F %T %Z")
|
||||
seconds_ca_expiration_date=$(TZ=$ca_zone $date_cmd -j -z "Europe/Paris" "$posix_ca_expiration_date" +"%s")
|
||||
|
||||
elif [ "$SYSTEM" = "openbsd" ]; then
|
||||
test_cert_expiration $seconds_cert_expiration_date
|
||||
test_ca_expiration $seconds_ca_expiration_date
|
||||
|
||||
# Cert expiration date for POSIX date, human formated then in seconds
|
||||
posix_cert_expiration_date=$(echo "$cert_expiration_date" | awk '{ printf $4" "(index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3" "$2" ",split($3,time,":"); print time[1],time[2],time[3]}' | awk '{printf "%04d%02d%02d%02d%02d.%02d\n", $1, $2, $3, $4, $5, $6}')
|
||||
cert_zone=$(echo "$cert_expiration_date" | awk '{print $5}')
|
||||
formatted_cert_expiration_date=$(TZ=$cert_zone $date_cmd -j -z "Europe/Paris" "$posix_cert_expiration_date" +"%F %T %Z")
|
||||
seconds_cert_expiration_date=$(TZ=$cert_zone $date_cmd -j -z "Europe/Paris" "$posix_cert_expiration_date" +"%s")
|
||||
if [ $is_backup_not_running_openvpn -eq "0" ]; then
|
||||
test_openvpn_restarted_since_last_ca_cert_modification 0
|
||||
else
|
||||
# Last OpenVPN restart in POSIX format, then in seconds
|
||||
posix_last_openvpn_restart_date=$(echo "$last_openvpn_restart_date" | awk '{ printf $4" "(index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3" "$2" ",split($3,time,":"); print time[1],time[2],time[3]}' | awk '{printf "%04d%02d%02d%02d%02d.%02d\n", $1, $2, $3, $4, $5, $6}')
|
||||
seconds_last_openvpn_restart_date=$($date_cmd -j "$posix_last_openvpn_restart_date" +%s)
|
||||
|
||||
# CA expiration date for POSIX date, human formated then in seconds
|
||||
posix_ca_expiration_date=$(echo "$ca_expiration_date" | awk '{ printf $4" "(index("JanFebMarAprMayJunJulAugSepOctNovDec",$1)+2)/3" "$2" ",split($3,time,":"); print time[1],time[2],time[3]}' | awk '{printf "%04d%02d%02d%02d%02d.%02d\n", $1, $2, $3, $4, $5, $6}')
|
||||
ca_zone=$(echo "$ca_expiration_date" | awk '{print $5}')
|
||||
formatted_ca_expiration_date=$(TZ=$ca_zone $date_cmd -j -z "Europe/Paris" "$posix_ca_expiration_date" +"%F %T %Z")
|
||||
seconds_ca_expiration_date=$(TZ=$ca_zone $date_cmd -j -z "Europe/Paris" "$posix_ca_expiration_date" +"%s")
|
||||
test_openvpn_restarted_since_last_ca_cert_modification $seconds_last_openvpn_restart_date
|
||||
fi
|
||||
|
||||
test_cert_expiration $seconds_cert_expiration_date
|
||||
test_ca_expiration $seconds_ca_expiration_date
|
||||
# If neither Linux nor BSD
|
||||
else
|
||||
|
||||
echo "CRITICAL - OS not supported"
|
||||
STATE=$STATE_CRITICAL
|
||||
exit $STATE
|
||||
|
||||
fi
|
||||
|
||||
if [ $RESTART_STATE -gt $STATE_OK ]; then
|
||||
echo $RESTART_ECHO
|
||||
echo $CERT_ECHO
|
||||
echo $CA_ECHO
|
||||
exit $RESTART_STATE
|
||||
else
|
||||
# Display the first one that expires first
|
||||
if [ $CA_STATE -gt $CERT_STATE ]; then
|
||||
echo $CA_ECHO
|
||||
echo $CERT_ECHO
|
||||
echo $RESTART_ECHO
|
||||
exit $CA_STATE
|
||||
elif [ $CERT_STATE -gt $CA_STATE ]; then
|
||||
echo $CERT_ECHO
|
||||
echo $CA_ECHO
|
||||
echo $RESTART_ECHO
|
||||
exit $CERT_STATE
|
||||
else
|
||||
echo $CERT_ECHO
|
||||
echo $CA_ECHO
|
||||
echo $RESTART_ECHO
|
||||
exit $CERT_STATE
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# If neither Linux nor BSD
|
||||
else
|
||||
|
||||
echo "CRITICAL - OS not supported"
|
||||
STATE=$STATE_CRITICAL
|
||||
exit $STATE
|
||||
|
||||
fi
|
||||
|
||||
# Display the first one that expires first
|
||||
if [ $CA_STATE -gt $CERT_STATE ]; then
|
||||
echo $CA_ECHO
|
||||
echo $CERT_ECHO
|
||||
exit $CA_STATE
|
||||
elif [ $CERT_STATE -gt $CA_STATE ]; then
|
||||
echo $CERT_ECHO
|
||||
echo $CA_ECHO
|
||||
exit $CERT_STATE
|
||||
else
|
||||
echo $CERT_ECHO
|
||||
echo $CA_ECHO
|
||||
exit $CERT_STATE
|
||||
fi
|
||||
main
|
||||
|
|
215
openvpn/files/check_openvpn_openbsd.pl
Normal file
215
openvpn/files/check_openvpn_openbsd.pl
Normal file
|
@ -0,0 +1,215 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# Copyright (c) 2007 Jaime Gascon Romero <jgascon@gmail.com>
|
||||
#
|
||||
# License Information:
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# $Id: check_openvpn.pl,v 1.1 2014/09/29 08:39:24 rdessort Exp $
|
||||
# $Revision: 1.1 $
|
||||
# Home Site: http://emergeworld.blogspot.com/
|
||||
# #####################################################################
|
||||
|
||||
use diagnostics;
|
||||
use strict;
|
||||
use Net::Telnet ();
|
||||
use Getopt::Long qw(:config no_ignore_case);
|
||||
use vars qw($PROGNAME $VERSION);
|
||||
use lib "/usr/local/libexec/nagios/";
|
||||
use utils qw(%ERRORS);
|
||||
|
||||
$PROGNAME = "check_openvpn";
|
||||
$VERSION = '$Revision: 1.1 $';
|
||||
|
||||
$ENV{'PATH'}='';
|
||||
$ENV{'BASH_ENV'}='';
|
||||
$ENV{'ENV'}='';
|
||||
|
||||
my ($opt_h, $opt_H, $opt_p, $opt_P, $opt_t, $opt_i, $opt_n, $opt_c, $opt_w, $opt_C, $opt_r);
|
||||
|
||||
sub print_help ();
|
||||
sub print_usage ();
|
||||
|
||||
GetOptions
|
||||
("h" => \$opt_h, "help" => \$opt_h,
|
||||
"H=s" => \$opt_H, "host=s" => \$opt_H,
|
||||
"p=i" => \$opt_p, "port=i" => \$opt_p,
|
||||
"P=s" => \$opt_P, "password=s" => \$opt_P,
|
||||
"t=i" => \$opt_t, "timeout=i" => \$opt_t,
|
||||
"i" => \$opt_i, "ip" => \$opt_i,
|
||||
"n" => \$opt_n, "numeric" => \$opt_n,
|
||||
"c" => \$opt_c, "critical" => \$opt_c,
|
||||
"w" => \$opt_w, "warning" => \$opt_w,
|
||||
"C=s" => \$opt_C, "common_name=s" => \$opt_C,
|
||||
"r=s" => \$opt_r, "remote_ip=s" => \$opt_r,
|
||||
) or exit $ERRORS{'UNKNOWN'};
|
||||
|
||||
# default values
|
||||
unless ( defined $opt_t ) {
|
||||
$opt_t = 10;
|
||||
}
|
||||
|
||||
if ($opt_h) {print_help(); exit $ERRORS{'OK'};}
|
||||
|
||||
if ( ! defined($opt_H) || ! defined($opt_p) ) {
|
||||
print_usage();
|
||||
exit $ERRORS{'UNKNOWN'}
|
||||
}
|
||||
|
||||
my @lines;
|
||||
my @clients;
|
||||
my @clients_ip;
|
||||
my $t;
|
||||
|
||||
eval {
|
||||
$t = new Net::Telnet (Timeout => $opt_t,
|
||||
Port => $opt_p,
|
||||
Prompt => '/END$/'
|
||||
);
|
||||
$t->open($opt_H);
|
||||
if ( defined $opt_P ) {
|
||||
$t->waitfor('/ENTER PASSWORD:$/');
|
||||
$t->print($opt_P);
|
||||
}
|
||||
$t->waitfor('/^$/');
|
||||
@lines = $t->cmd("status 2");
|
||||
$t->close;
|
||||
};
|
||||
|
||||
if ($@) {
|
||||
print "OpenVPN Critical: Can't connect to server\n";
|
||||
exit $ERRORS{'CRITICAL'};
|
||||
}
|
||||
|
||||
|
||||
if (defined $opt_i || defined $opt_r) {
|
||||
foreach (@lines) {
|
||||
if ($_ =~ /CLIENT_LIST,.*,(\d+\.\d+\.\d+\.\d+):\d+,/) {
|
||||
push @clients_ip, $1;
|
||||
}
|
||||
}
|
||||
if (defined $opt_i) {
|
||||
print "OpenVPN OK: "."@clients_ip ";
|
||||
exit $ERRORS{'OK'};
|
||||
} elsif (defined $opt_r) {
|
||||
if ( ! grep /\b$opt_r\b/, @clients_ip) {
|
||||
if (defined $opt_c) {
|
||||
print "OpenVPN CRITICAL: $opt_r don't found";
|
||||
exit $ERRORS{'CRITICAL'};
|
||||
} else {
|
||||
print "OpenVPN WARNING: $opt_r don't found";
|
||||
exit $ERRORS{'WARNING'};
|
||||
}
|
||||
}
|
||||
print "OpenVPN OK: "."@clients_ip ";
|
||||
exit $ERRORS{'OK'};
|
||||
}
|
||||
}
|
||||
|
||||
foreach (@lines) {
|
||||
if ($_ =~ /CLIENT_LIST,(.*),\d+\.\d+\.\d+\.\d+:\d+,/) {
|
||||
push @clients, $1;
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $opt_C) {
|
||||
if ( ! grep /\b$opt_C\b/, @clients) {
|
||||
if (defined $opt_c) {
|
||||
print "OpenVPN CRITICAL: $opt_C don't found";
|
||||
exit $ERRORS{'CRITICAL'};
|
||||
} else {
|
||||
print "OpenVPN WARNING: $opt_C don't found";
|
||||
exit $ERRORS{'WARNING'};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (defined $opt_n) {
|
||||
print "OpenVPN OK: ".@clients." connected clients.";
|
||||
exit $ERRORS{'OK'};
|
||||
}
|
||||
|
||||
print "OpenVPN OK: "."@clients ";
|
||||
exit $ERRORS{'OK'};
|
||||
|
||||
#######################################################################
|
||||
###### Subroutines ####################################################
|
||||
|
||||
sub print_usage() {
|
||||
print "Usage: $PROGNAME -H | --host <IP or hostname> -p | --port <port number> [-P | --password] <password> [-t | --timeout] <timeout in seconds>
|
||||
[-i | --ip] [-n | --numeric] [-C | --common_name] <common_name> [-r | --remote_ip] <remote_ip> [-c | --critical] [-w | --warning]\n\n";
|
||||
print " $PROGNAME [-h | --help]\n";
|
||||
}
|
||||
|
||||
sub print_help() {
|
||||
print "$PROGNAME $VERSION\n\n";
|
||||
print "Copyright (c) 2007 Jaime Gascon Romero
|
||||
|
||||
Nagios plugin to check the clients connected to a openvpn server.
|
||||
|
||||
";
|
||||
print_usage();
|
||||
print "
|
||||
-H | --host
|
||||
IP address or hostname of the openvpn server.
|
||||
|
||||
-p | --port
|
||||
Management port interface of the openvpn server.
|
||||
|
||||
-P | --password
|
||||
Password for the management interface of the openvpn server.
|
||||
|
||||
-t | --timeout
|
||||
Timeout for the connection attempt. Optional, default 10 seconds.
|
||||
|
||||
|
||||
Optional parameters
|
||||
===================
|
||||
|
||||
-i | --ip
|
||||
Prints the IP address of the remote client instead of the common name.
|
||||
|
||||
-n | --numeric
|
||||
Prints the number of clients connected to the openvpn server.
|
||||
|
||||
|
||||
Matching Parameters
|
||||
===================
|
||||
|
||||
-C | --common_name
|
||||
The common name, as it is specified in the client certificate, who is wanted to check.
|
||||
|
||||
-r | --remote_ip
|
||||
The client remote ip address who is wanted to check.
|
||||
|
||||
-c | --critical
|
||||
Exits with CRITICAL status if the client specified by the common name or the remote ip address is not connected.
|
||||
|
||||
-w | --warning
|
||||
Exits with WARNING status if the client specified by the common name or the remote ip address is not connected.
|
||||
|
||||
|
||||
Other Parameters
|
||||
================
|
||||
|
||||
-h | --help
|
||||
Show this help.
|
||||
";
|
||||
|
||||
}
|
||||
|
||||
# vim:sts=2:sw=2:ts=2:et
|
|
@ -4,3 +4,11 @@
|
|||
service:
|
||||
name: nagios-nrpe-server
|
||||
state: restarted
|
||||
|
||||
- name: restart nrpe
|
||||
service:
|
||||
name: nrpe
|
||||
state: restarted
|
||||
|
||||
- name: reload packetfilter
|
||||
command: pfctl -f /etc/pf.conf
|
||||
|
|
296
openvpn/tasks/debian.yml
Normal file
296
openvpn/tasks/debian.yml
Normal file
|
@ -0,0 +1,296 @@
|
|||
---
|
||||
|
||||
- name: Install OpenVPN
|
||||
apt:
|
||||
name: openvpn
|
||||
|
||||
- name: Delete unwanted OpenVPN folders
|
||||
file:
|
||||
state: absent
|
||||
dest: "/etc/openvpn/{{ item }}"
|
||||
with_items:
|
||||
- client
|
||||
- server
|
||||
|
||||
- name: Clone shellpki repo
|
||||
git:
|
||||
repo: "https://gitea.evolix.org/evolix/shellpki.git"
|
||||
dest: /root/shellpki
|
||||
|
||||
- name: Create the shellpki user
|
||||
user:
|
||||
name: shellpki
|
||||
system: yes
|
||||
create_home: no
|
||||
home: "/etc/shellpki"
|
||||
shell: "/usr/sbin/nologin"
|
||||
|
||||
- name: Create /etc/shellpki
|
||||
file:
|
||||
dest: "/etc/shellpki"
|
||||
mode: "0755"
|
||||
owner: shellpki
|
||||
group: shellpki
|
||||
state: directory
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Copy shellpki files
|
||||
copy:
|
||||
src: "{{ item.source }}"
|
||||
dest: "{{ item.destination }}"
|
||||
remote_src: yes
|
||||
with_items:
|
||||
- { source: "/root/shellpki/openssl.cnf", destination: "/etc/shellpki/openssl.cnf" }
|
||||
- { source: "/root/shellpki/shellpki", destination: "/usr/local/sbin/shellpki" }
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Change files permissions
|
||||
file:
|
||||
dest: "{{ item.dest }}"
|
||||
mode: "{{ item.mode }}"
|
||||
owner: "{{ item.owner }}"
|
||||
group: "{{ item.group }}"
|
||||
with_items:
|
||||
- { dest: "/etc/shellpki/openssl.cnf", mode: "0640", owner: "shellpki", group: "shellpki" }
|
||||
- { dest: "/usr/local/sbin/shellpki", mode: "0755", owner: "root", group: "root" }
|
||||
|
||||
- name: Delete local shellpki repo
|
||||
file:
|
||||
state: absent
|
||||
dest: "/root/shellpki"
|
||||
|
||||
- name: Add sudo rights
|
||||
lineinfile:
|
||||
dest: "/etc/sudoers.d/shellpki"
|
||||
regexp: '/usr/local/sbin/shellpki'
|
||||
line: "%shellpki ALL = (root) /usr/local/sbin/shellpki"
|
||||
create: yes
|
||||
mode: "0400"
|
||||
owner: root
|
||||
group: root
|
||||
validate: 'visudo -cf %s'
|
||||
|
||||
- name: Deploy OpenVPN client config template
|
||||
template:
|
||||
src: "ovpn.conf.j2"
|
||||
dest: "/etc/shellpki/ovpn.conf"
|
||||
mode: "0600"
|
||||
owner: shellpki
|
||||
group: shellpki
|
||||
|
||||
- name: Generate dhparam
|
||||
command: "openssl dhparam -out /etc/shellpki/dh2048.pem 2048"
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Fix CRL rights in shellpki command
|
||||
lineinfile:
|
||||
dest: "/usr/local/sbin/shellpki"
|
||||
regexp: '{{ item.regexp }}'
|
||||
insertafter: "{{ item.insertafter }}"
|
||||
line: "{{ item.line }}"
|
||||
with_items:
|
||||
- { regexp: '^ chmod 644 /etc/shellpki/crl.pem$', line: " chmod 644 /etc/shellpki/crl.pem", insertafter: '^ chmod 640 "\${CACERT}"$' }
|
||||
- { regexp: '^ chmod 755 /etc/shellpki/$', line: " chmod 755 /etc/shellpki/", insertafter: '^ chmod 644 /etc/shellpki/crl.pem$' }
|
||||
|
||||
- name: Deploy OpenVPN server config
|
||||
template:
|
||||
src: "server.conf.j2"
|
||||
dest: "/etc/openvpn/server.conf"
|
||||
mode: "0600"
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Is minifirewall installed ?
|
||||
stat:
|
||||
path: "/etc/default/minifirewall"
|
||||
check_mode: no
|
||||
changed_when: false
|
||||
register: minifirewall_config
|
||||
|
||||
- name: Retrieve the default interface
|
||||
shell: "grep '^INT=' /etc/default/minifirewall | cut -d\\' -f 2"
|
||||
check_mode: no
|
||||
changed_when: false
|
||||
register: minifirewall_int
|
||||
when: minifirewall_config.stat.exists
|
||||
|
||||
- name: Add minifirewall rule in config file
|
||||
lineinfile:
|
||||
dest: "/etc/default/minifirewall"
|
||||
line: "{{ item }}"
|
||||
with_items:
|
||||
- "# OpenVPN"
|
||||
- "/sbin/iptables -t nat -A POSTROUTING -s {{ openvpn_lan }}/{{ openvpn_netmask_cidr }} -o $INT -j MASQUERADE"
|
||||
when: minifirewall_config.stat.exists
|
||||
|
||||
- name: Activate minifirewall rule
|
||||
iptables:
|
||||
table: nat
|
||||
chain: POSTROUTING
|
||||
source: "{{ openvpn_lan }}/{{ openvpn_netmask_cidr }}"
|
||||
out_interface: "{{ minifirewall_int.stdout }}"
|
||||
jump: MASQUERADE
|
||||
when: minifirewall_config.stat.exists
|
||||
|
||||
- name: Add 1194/udp OpenVPN port to public services in minifirewall
|
||||
replace:
|
||||
dest: "/etc/default/minifirewall"
|
||||
regexp: "^SERVICESUDP1='(.*)?'$"
|
||||
replace: "SERVICESUDP1='\\1 1194'"
|
||||
backup: yes
|
||||
when: minifirewall_config.stat.exists
|
||||
|
||||
- name: Activate minifirewall rule for IPv4
|
||||
iptables:
|
||||
chain: INPUT
|
||||
protocol: udp
|
||||
destination_port: "1194"
|
||||
jump: ACCEPT
|
||||
ip_version: ipv4
|
||||
when: minifirewall_config.stat.exists
|
||||
|
||||
- name: Activate minifirewall rule for IPv6
|
||||
iptables:
|
||||
chain: INPUT
|
||||
protocol: udp
|
||||
destination_port: "1194"
|
||||
jump: ACCEPT
|
||||
ip_version: ipv6
|
||||
when: minifirewall_config.stat.exists
|
||||
|
||||
- name: Enable forwarding
|
||||
sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: "1"
|
||||
sysctl_file: "/etc/sysctl.d/openvpn.conf"
|
||||
|
||||
- name: Generate a password for the management interface
|
||||
set_fact:
|
||||
management_pwd: "{{ lookup('password', '/dev/null length=15 chars=ascii_letters,digits') }}"
|
||||
|
||||
- name: Set the management password
|
||||
copy:
|
||||
dest: "/etc/openvpn/management-pwd"
|
||||
content: "{{ management_pwd }}"
|
||||
mode: "0600"
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Enable openvpn service
|
||||
systemd:
|
||||
name: "openvpn@server.service"
|
||||
enabled: yes
|
||||
|
||||
- name: Is NRPE installed ?
|
||||
stat:
|
||||
path: "/etc/nagios/nrpe.d/evolix.cfg"
|
||||
check_mode: no
|
||||
changed_when: false
|
||||
register: nrpe_evolix_config
|
||||
|
||||
- name: Install NRPE check dependencies
|
||||
apt:
|
||||
name: libnet-telnet-perl
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Install OpenVPN NRPE check
|
||||
copy:
|
||||
src: "files/check_openvpn_debian.pl"
|
||||
dest: "/usr/local/lib/nagios/plugins/check_openvpn"
|
||||
mode: "0755"
|
||||
owner: root
|
||||
group: nagios
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- name: Configure NRPE OpenVPN check
|
||||
lineinfile:
|
||||
dest: "/etc/nagios/nrpe.d/evolix.cfg"
|
||||
regexp: '^command\[check_openvpn\]='
|
||||
line: "command[check_openvpn]=/usr/local/lib/nagios/plugins/check_openvpn -H 127.0.0.1 -p 1195 -P {{ management_pwd }}"
|
||||
notify: restart nagios-nrpe-server
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Install OpenVPN certificates NRPE check
|
||||
copy:
|
||||
src: "files/check_openvpn_certificates.sh"
|
||||
dest: "/usr/local/lib/nagios/plugins/check_openvpn_certificates.sh"
|
||||
mode: "0755"
|
||||
owner: root
|
||||
group: nagios
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- name: Add sudo rights for NRPE check
|
||||
lineinfile:
|
||||
dest: "/etc/sudoers.d/openvpn"
|
||||
regexp: 'check_openvpn_certificates.sh'
|
||||
line: "nagios ALL=NOPASSWD: /usr/local/lib/nagios/plugins/check_openvpn_certificates.sh"
|
||||
create: yes
|
||||
mode: "0400"
|
||||
owner: root
|
||||
group: root
|
||||
validate: 'visudo -cf %s'
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- name: Configure NRPE certificates check
|
||||
lineinfile:
|
||||
dest: "/etc/nagios/nrpe.d/evolix.cfg"
|
||||
regexp: '^command\[check_openvpn_certificates\]='
|
||||
line: "command[check_openvpn_certificates]=sudo /usr/local/lib/nagios/plugins/check_openvpn_certificates.sh"
|
||||
notify: restart nagios-nrpe-server
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
# BEGIN TODO : Get this script from master branch when cloning it at the beginning when dev branch is merged with master (this script is currently not available on master branch)
|
||||
- name: Clone dev branch of shellpki repo
|
||||
git:
|
||||
repo: "https://gitea.evolix.org/evolix/shellpki.git"
|
||||
dest: /root/shellpki-dev
|
||||
version: dev
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Copy shellpki script
|
||||
copy:
|
||||
src: "/root/shellpki-dev/cert-expirations.sh"
|
||||
dest: "/usr/share/scripts/cert-expirations.sh"
|
||||
mode: "0700"
|
||||
owner: root
|
||||
group: root
|
||||
remote_src: yes
|
||||
|
||||
- name: Delete local shellpki-dev repo
|
||||
file:
|
||||
state: absent
|
||||
dest: "/root/shellpki-dev"
|
||||
# END TODO
|
||||
|
||||
- name: Install cron to warn about certificates expiration
|
||||
cron:
|
||||
name: "OpenVPN certificates expiration"
|
||||
special_time: monthly
|
||||
job: '/usr/share/scripts/cert-expirations.sh | mail -E -s "PKI VPN {{ ansible_hostname }} : recapitulatif expirations" {{ client_email }}'
|
||||
|
||||
- name: Warn the user about command to execute manually
|
||||
pause:
|
||||
prompt: |
|
||||
/!\ WARNING /!\
|
||||
You have to manually create the CA on the server with "shellpki init {{ ansible_fqdn }}". The command will ask you to create a password, and will ask you again to give the same one several times.
|
||||
You have to manually generate the CRL on the server with "openssl ca -gencrl -keyfile /etc/shellpki/cakey.key -cert /etc/shellpki/cacert.pem -out /etc/shellpki/crl.pem -config /etc/shellpki/openssl.cnf". The previously created password will be asked.
|
||||
You have to manually create the server's certificate with "shellpki create {{ ansible_fqdn }}".
|
||||
You have to adjust the config file "/etc/openvpn/server.conf" for the following parameters : local (to check), cert (to check), key (to add), server (to check), push (to complete if needed).
|
||||
Finally, you can (re)start the OpenVPN service with "systemctl restart openvpn@server.service".
|
||||
|
||||
Press enter to exit when it's done.
|
||||
|
|
@ -1,285 +1,15 @@
|
|||
---
|
||||
|
||||
- name: This role is only compatible with Debian
|
||||
- name: System compatibility checks
|
||||
assert:
|
||||
that: "ansible_distribution == 'Debian'"
|
||||
msg: "Only compatible with Debian"
|
||||
that: "ansible_distribution == 'Debian' or ansible_distribution == 'OpenBSD'"
|
||||
msg: "Only compatible with Debian and OpenBSD"
|
||||
|
||||
- name: Install OpenVPN
|
||||
apt:
|
||||
name: openvpn
|
||||
- name: Include Debian version
|
||||
include: debian.yml
|
||||
when: ansible_distribution == "Debian"
|
||||
|
||||
- name: Delete unwanted OpenVPN folders
|
||||
file:
|
||||
state: absent
|
||||
path: "/etc/openvpn/{{ item }}"
|
||||
with_items:
|
||||
- client
|
||||
- server
|
||||
|
||||
- name: Clone shellpki repo
|
||||
git:
|
||||
repo: "https://gitea.evolix.org/evolix/shellpki.git"
|
||||
dest: /root/shellpki
|
||||
|
||||
- name: Create the shellpki user
|
||||
user:
|
||||
name: shellpki
|
||||
system: yes
|
||||
create_home: no
|
||||
home: "/etc/shellpki"
|
||||
shell: "/usr/sbin/nologin"
|
||||
|
||||
- name: Create /etc/shellpki
|
||||
file:
|
||||
path: "/etc/shellpki"
|
||||
state: directory
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Copy shellpki files
|
||||
copy:
|
||||
src: "{{ item.source }}"
|
||||
dest: "{{ item.destination }}"
|
||||
remote_src: yes
|
||||
with_items:
|
||||
- { source: "/root/shellpki/openssl.cnf", destination: "/etc/shellpki/openssl.cnf" }
|
||||
- { source: "/root/shellpki/shellpki", destination: "/usr/local/sbin/shellpki" }
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Change files permissions
|
||||
file:
|
||||
path: "{{ item.path }}"
|
||||
mode: "{{ item.mode }}"
|
||||
with_items:
|
||||
- { path: "/etc/shellpki/openssl.cnf", mode: "0640" }
|
||||
- { path: "/usr/local/sbin/shellpki", mode: "0755" }
|
||||
|
||||
- name: Delete local shellpki repo
|
||||
file:
|
||||
state: absent
|
||||
path: "/root/shellpki"
|
||||
|
||||
- name: Change directory owner
|
||||
file:
|
||||
path: "/etc/shellpki"
|
||||
owner: shellpki
|
||||
recurse: yes
|
||||
state: directory
|
||||
|
||||
- name: Add sudo rights
|
||||
lineinfile:
|
||||
dest: "/etc/sudoers.d/shellpki"
|
||||
regexp: '/usr/local/sbin/shellpki'
|
||||
line: "%shellpki ALL = (root) /usr/local/sbin/shellpki"
|
||||
create: yes
|
||||
validate: 'visudo -cf %s'
|
||||
|
||||
- name: Deploy OpenVPN client config template
|
||||
template:
|
||||
src: "ovpn.conf.j2"
|
||||
dest: "/etc/shellpki/ovpn.conf"
|
||||
|
||||
- name: Generate dhparam
|
||||
command: "openssl dhparam -out /etc/shellpki/dh2048.pem 2048"
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Fix CRL rights in shellpki command
|
||||
lineinfile:
|
||||
path: "/usr/local/sbin/shellpki"
|
||||
regexp: '{{ item.regexp }}'
|
||||
insertafter: "{{ item.insertafter }}"
|
||||
line: "{{ item.line }}"
|
||||
with_items:
|
||||
- { regexp: '^ chmod 644 /etc/shellpki/crl.pem$', line: " chmod 644 /etc/shellpki/crl.pem", insertafter: '^ chmod 640 "\${CACERT}"$' }
|
||||
- { regexp: '^ chmod 755 /etc/shellpki/$', line: " chmod 755 /etc/shellpki/", insertafter: '^ chmod 644 /etc/shellpki/crl.pem$' }
|
||||
|
||||
- name: Deploy OpenVPN server config
|
||||
template:
|
||||
src: "server.conf.j2"
|
||||
dest: "/etc/openvpn/server.conf"
|
||||
|
||||
- name: Is minifirewall installed ?
|
||||
stat:
|
||||
path: "/etc/default/minifirewall"
|
||||
check_mode: no
|
||||
register: minifirewall_config
|
||||
|
||||
- name: Retrieve the default interface
|
||||
shell: "grep '^INT=' /etc/default/minifirewall | cut -d\\' -f 2"
|
||||
check_mode: no
|
||||
changed_when: false
|
||||
register: minifirewall_int
|
||||
|
||||
- name: Add minifirewall rule in config file
|
||||
lineinfile:
|
||||
path: "/etc/default/minifirewall"
|
||||
line: "{{ item }}"
|
||||
with_items:
|
||||
- "# OpenVPN"
|
||||
- "/sbin/iptables -t nat -A POSTROUTING -s {{ openvpn_lan }}/{{ openvpn_netmask_cidr }} -o $INT -j MASQUERADE"
|
||||
when: minifirewall_config.stat.exists
|
||||
|
||||
- name: Activate minifirewall rule
|
||||
iptables:
|
||||
table: nat
|
||||
chain: POSTROUTING
|
||||
source: "{{ openvpn_lan }}/{{ openvpn_netmask_cidr }}"
|
||||
out_interface: "{{ minifirewall_int.stdout }}"
|
||||
jump: MASQUERADE
|
||||
when: minifirewall_config.stat.exists
|
||||
|
||||
- name: Add 1194/udp OpenVPN port to public services in minifirewall
|
||||
replace:
|
||||
path: "/etc/default/minifirewall"
|
||||
regexp: "^SERVICESUDP1='(.*)?'$"
|
||||
replace: "SERVICESUDP1='\\1 1194'"
|
||||
backup: yes
|
||||
when: minifirewall_config.stat.exists
|
||||
|
||||
- name: Activate minifirewall rule for IPv4
|
||||
iptables:
|
||||
chain: INPUT
|
||||
protocol: udp
|
||||
destination_port: "1194"
|
||||
jump: ACCEPT
|
||||
ip_version: ipv4
|
||||
when: minifirewall_config.stat.exists
|
||||
|
||||
- name: Activate minifirewall rule for IPv6
|
||||
iptables:
|
||||
chain: INPUT
|
||||
protocol: udp
|
||||
destination_port: "1194"
|
||||
jump: ACCEPT
|
||||
ip_version: ipv6
|
||||
when: minifirewall_config.stat.exists
|
||||
|
||||
- name: Enable forwarding
|
||||
sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: "1"
|
||||
sysctl_file: "/etc/sysctl.d/openvpn.conf"
|
||||
|
||||
- name: Generate a password for the management interface
|
||||
set_fact:
|
||||
management_pwd: "{{ lookup('password', '/dev/null length=15 chars=ascii_letters,digits') }}"
|
||||
|
||||
- name: Set the management password
|
||||
copy:
|
||||
dest: "/etc/openvpn/management-pwd"
|
||||
content: "{{ management_pwd }}"
|
||||
|
||||
- name: Enable openvpn service
|
||||
systemd:
|
||||
name: "openvpn@server.service"
|
||||
enabled: yes
|
||||
|
||||
- name: Is NRPE installed ?
|
||||
stat:
|
||||
path: "/etc/nagios/nrpe.d/evolix.cfg"
|
||||
check_mode: no
|
||||
register: nrpe_evolix_config
|
||||
|
||||
- name: Install NRPE check dependencies
|
||||
apt:
|
||||
name: libnet-telnet-perl
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Install OpenVPN NRPE check
|
||||
copy:
|
||||
src: "files/check_openvpn.pl"
|
||||
dest: "/usr/local/lib/nagios/plugins/check_openvpn"
|
||||
mode: "0755"
|
||||
owner: root
|
||||
group: nagios
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- name: Add NRPE check
|
||||
lineinfile:
|
||||
dest: "/etc/nagios/nrpe.d/evolix.cfg"
|
||||
regexp: '^command\[check_openvpn\]='
|
||||
line: "command[check_openvpn]=/usr/local/lib/nagios/plugins/check_openvpn -H 127.0.0.1 -p 1195 -P {{ management_pwd }}"
|
||||
notify: restart nagios-nrpe-server
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Install OpenVPN certificates NRPE check
|
||||
copy:
|
||||
src: "files/check_openvpn_certificates.sh"
|
||||
dest: "/usr/local/lib/nagios/plugins/check_openvpn_certificates.sh"
|
||||
mode: "0755"
|
||||
owner: root
|
||||
group: nagios
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- name: Add sudo rights for NRPE check
|
||||
lineinfile:
|
||||
dest: "/etc/sudoers.d/openvpn"
|
||||
regexp: 'check_openvpn_certificates.sh'
|
||||
line: "nagios ALL=NOPASSWD: /usr/local/lib/nagios/plugins/check_openvpn_certificates.sh"
|
||||
create: yes
|
||||
validate: 'visudo -cf %s'
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- name: Add NRPE check
|
||||
lineinfile:
|
||||
dest: "/etc/nagios/nrpe.d/evolix.cfg"
|
||||
regexp: '^command\[check_openvpn_certificates\]='
|
||||
line: "command[check_openvpn_certificates]=sudo /usr/local/lib/nagios/plugins/check_openvpn_certificates.sh"
|
||||
notify: restart nagios-nrpe-server
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
# BEGIN TODO : Get this script from master branch when cloning it at the beginning when dev branch is merged with master (this script is currently not available on master branch)
|
||||
- name: Clone dev branch of shellpki repo
|
||||
git:
|
||||
repo: "https://gitea.evolix.org/evolix/shellpki.git"
|
||||
dest: /root/shellpki-dev
|
||||
version: dev
|
||||
|
||||
- include_role:
|
||||
name: evolix/remount-usr
|
||||
|
||||
- name: Copy shellpki script
|
||||
copy:
|
||||
src: "/root/shellpki-dev/cert-expirations.sh"
|
||||
dest: "/usr/share/scripts/cert-expirations.sh"
|
||||
mode: "0700"
|
||||
# owner: root
|
||||
# group: root
|
||||
remote_src: yes
|
||||
|
||||
- name: Delete local shellpki-dev repo
|
||||
file:
|
||||
state: absent
|
||||
path: "/root/shellpki-dev"
|
||||
# END TODO
|
||||
|
||||
- name: Install cron to warn about certificates expiration
|
||||
cron:
|
||||
name: "OpenVPN certificates expiration"
|
||||
special_time: monthly
|
||||
job: '/usr/share/scripts/cert-expirations.sh | mail -E -s "PKI VPN {{ ansible_hostname }} : recapitulatif expirations" {{ client_email }}'
|
||||
|
||||
- name: Warn the user about command to execute manually
|
||||
pause:
|
||||
prompt: |
|
||||
/!\ WARNING /!\
|
||||
You have to manually create the CA on the server with "shellpki init {{ ansible_fqdn }}". The command will ask you to create a password, and will ask you again to give the same one several times.
|
||||
You have to manually generate the CRL on the server with "openssl ca -gencrl -keyfile /etc/shellpki/cakey.key -cert /etc/shellpki/cacert.pem -out /etc/shellpki/crl.pem -config /etc/shellpki/openssl.cnf". The previously created password will be asked
|
||||
You have to manually create the server's certificate with "shellpki create {{ ansible_fqdn }}"
|
||||
You have to adjuste the config file "/etc/openvpn/server.conf" for the following parameters : local (to check), cert (to check), key (to add), server (to check), push (to complete if needed).
|
||||
Finally, you can (re)start the OpenVPN service with "systemctl restart openvpn@server.service"
|
||||
|
||||
Press enter to exit when it's done.
|
||||
- name: Include OpenBSD version
|
||||
include: openbsd.yml
|
||||
when: ansible_distribution == "OpenBSD"
|
||||
|
||||
|
|
235
openvpn/tasks/openbsd.yml
Normal file
235
openvpn/tasks/openbsd.yml
Normal file
|
@ -0,0 +1,235 @@
|
|||
---
|
||||
|
||||
- name: Install OpenVPN
|
||||
openbsd_pkg:
|
||||
name: openvpn--
|
||||
when: ansible_distribution == 'OpenBSD'
|
||||
|
||||
- name: Create /etc/openvpn
|
||||
file:
|
||||
dest: "/etc/openvpn"
|
||||
state: directory
|
||||
owner: root
|
||||
group: wheel
|
||||
mode: "0755"
|
||||
|
||||
- name: Clone shellpki repo
|
||||
git:
|
||||
repo: "https://gitea.evolix.org/evolix/shellpki.git"
|
||||
dest: /root/shellpki
|
||||
|
||||
- name: Create the shellpki user
|
||||
user:
|
||||
name: _shellpki
|
||||
system: yes
|
||||
create_home: no
|
||||
home: "/etc/shellpki"
|
||||
shell: "/sbin/nologin"
|
||||
|
||||
- name: Create /etc/shellpki
|
||||
file:
|
||||
dest: "/etc/shellpki"
|
||||
state: directory
|
||||
owner: _shellpki
|
||||
group: _shellpki
|
||||
mode: "0755"
|
||||
|
||||
- name: Copy shellpki files
|
||||
copy:
|
||||
src: "{{ item.source }}"
|
||||
dest: "{{ item.destination }}"
|
||||
remote_src: yes
|
||||
with_items:
|
||||
- { source: "/root/shellpki/openssl.cnf", destination: "/etc/shellpki/openssl.cnf" }
|
||||
- { source: "/root/shellpki/shellpki", destination: "/usr/local/sbin/shellpki" }
|
||||
|
||||
- name: Change files permissions
|
||||
file:
|
||||
dest: "{{ item.dest }}"
|
||||
mode: "{{ item.mode }}"
|
||||
owner: "{{ item.owner }}"
|
||||
group: "{{ item.group }}"
|
||||
with_items:
|
||||
- { dest: "/etc/shellpki/openssl.cnf", mode: "0640", owner: "_shellpki", group: "_shellpki"}
|
||||
- { dest: "/usr/local/sbin/shellpki", mode: "0755", owner: "root", group: "wheel" }
|
||||
|
||||
- name: Delete local shellpki repo
|
||||
file:
|
||||
state: absent
|
||||
dest: "/root/shellpki"
|
||||
|
||||
- name: Add sudo rights
|
||||
lineinfile:
|
||||
dest: "/etc/sudoers"
|
||||
regexp: '/usr/local/sbin/shellpki'
|
||||
line: "%_shellpki ALL = (root) /usr/local/sbin/shellpki"
|
||||
validate: 'visudo -cf %s'
|
||||
|
||||
- name: Deploy OpenVPN client config template
|
||||
template:
|
||||
src: "ovpn.conf.j2"
|
||||
dest: "/etc/shellpki/ovpn.conf"
|
||||
mode: "0640"
|
||||
owner: _shellpki
|
||||
group: _shellpki
|
||||
|
||||
- name: Generate dhparam
|
||||
command: "openssl dhparam -out /etc/shellpki/dh2048.pem 2048"
|
||||
|
||||
- name: Fix CRL rights in shellpki command
|
||||
lineinfile:
|
||||
dest: "/usr/local/sbin/shellpki"
|
||||
regexp: '{{ item.regexp }}'
|
||||
insertafter: "{{ item.insertafter }}"
|
||||
line: "{{ item.line }}"
|
||||
with_items:
|
||||
- { regexp: '^ chmod 644 /etc/shellpki/crl.pem$', line: " chmod 644 /etc/shellpki/crl.pem", insertafter: '^ chmod 640 "\${CACERT}"$' }
|
||||
- { regexp: '^ chmod 755 /etc/shellpki/$', line: " chmod 755 /etc/shellpki/", insertafter: '^ chmod 644 /etc/shellpki/crl.pem$' }
|
||||
|
||||
- name: Deploy OpenVPN server config
|
||||
template:
|
||||
src: "server.conf.j2"
|
||||
dest: "/etc/openvpn/server.conf"
|
||||
mode: "0600"
|
||||
owner: root
|
||||
group: wheel
|
||||
|
||||
- name: Configure PacketFilter
|
||||
lineinfile:
|
||||
dest: "/etc/pf.conf"
|
||||
line: "{{ item }}"
|
||||
validate: 'pfctl -nf %s'
|
||||
notify: reload packetfilter
|
||||
with_items:
|
||||
- "# OpenVPN"
|
||||
- "pass in quick on $ext_if proto udp from any to self port 1194"
|
||||
|
||||
- name: Create a cron to rotate the logs
|
||||
cron:
|
||||
name: "OpenVPN logs rotation"
|
||||
weekday: "6"
|
||||
hour: "4"
|
||||
minute: "0"
|
||||
job: "cp /var/log/openvpn.log /var/log/openvpn.log.$(date +\\%F) && echo \"$(date +\\%F' '\\%R) - logfile turned over via cron\" > /var/log/openvpn.log && gzip /var/log/openvpn.log.$(date +\\%F) && find /var/log/ -type f -name \"openvpn.log.*\" -mtime +365 -exec rm {} \\+"
|
||||
|
||||
- name: Generate a password for the management interface
|
||||
set_fact:
|
||||
management_pwd: "{{ lookup('password', '/dev/null length=15 chars=ascii_letters,digits') }}"
|
||||
|
||||
- name: Set the management password
|
||||
copy:
|
||||
dest: "/etc/openvpn/management-pwd"
|
||||
content: "{{ management_pwd }}"
|
||||
mode: "0600"
|
||||
owner: root
|
||||
group: wheel
|
||||
|
||||
- name: Enable openvpn service
|
||||
service:
|
||||
name: openvpn
|
||||
enabled: yes
|
||||
|
||||
- name: Set openvpn flags
|
||||
lineinfile:
|
||||
dest: /etc/rc.conf.local
|
||||
regexp: "^openvpn_flags="
|
||||
line: "openvpn_flags=--daemon --config /etc/openvpn/server.conf"
|
||||
create: yes
|
||||
|
||||
- name: Is NRPE installed ?
|
||||
stat:
|
||||
path: "/etc/nrpe.d/evolix.cfg"
|
||||
check_mode: no
|
||||
register: nrpe_evolix_config
|
||||
|
||||
- name: Install NRPE check dependencies
|
||||
openbsd_pkg:
|
||||
name: p5-Net-Telnet
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- name: Install OpenVPN NRPE check
|
||||
copy:
|
||||
src: "files/check_openvpn_openbsd.pl"
|
||||
dest: "/usr/local/libexec/nagios/plugins/check_openvpn.pl"
|
||||
mode: "0755"
|
||||
owner: root
|
||||
group: wheel
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- name: Configure NRPE OpenVPN check
|
||||
lineinfile:
|
||||
dest: "/etc/nrpe.d/zzz_evolix.cfg"
|
||||
regexp: '^command\[check_openvpn\]='
|
||||
line: "command[check_openvpn]=/usr/local/libexec/nagios/plugins/check_openvpn.pl -H 127.0.0.1 -p 1195 -P {{ management_pwd }}"
|
||||
create: yes
|
||||
mode: "0644"
|
||||
owner: root
|
||||
group: wheel
|
||||
notify: restart nrpe
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- name: Install OpenVPN certificates NRPE check
|
||||
copy:
|
||||
src: "files/check_openvpn_certificates.sh"
|
||||
dest: "/usr/local/libexec/nagios/plugins/check_openvpn_certificates.sh"
|
||||
mode: "0755"
|
||||
owner: root
|
||||
group: wheel
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- name: Add doas rights for NRPE check
|
||||
lineinfile:
|
||||
dest: "/etc/doas.conf"
|
||||
regexp: 'check_openvpn_certificates.sh'
|
||||
line: "permit nopass _nrpe as root cmd /usr/local/libexec/nagios/plugins/check_openvpn_certificates.sh"
|
||||
validate: 'doas -C %s'
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
- name: Configure NRPE certificates check
|
||||
lineinfile:
|
||||
dest: "/etc/nrpe.d/evolix.cfg"
|
||||
regexp: '^command\[check_openvpn_certificates\]='
|
||||
line: "command[check_openvpn_certificates]=doas /usr/local/libexec/nagios/plugins/check_openvpn_certificates.sh"
|
||||
notify: restart nrpe
|
||||
when: nrpe_evolix_config.stat.exists
|
||||
|
||||
# BEGIN TODO : Get this script from master branch when cloning it at the beginning when dev branch is merged with master (this script is currently not available on master branch)
|
||||
- name: Clone dev branch of shellpki repo
|
||||
git:
|
||||
repo: "https://gitea.evolix.org/evolix/shellpki.git"
|
||||
dest: /root/shellpki-dev
|
||||
version: dev
|
||||
|
||||
- name: Copy shellpki script
|
||||
copy:
|
||||
src: "/root/shellpki-dev/cert-expirations.sh"
|
||||
dest: "/usr/share/scripts/cert-expirations.sh"
|
||||
mode: "0700"
|
||||
owner: root
|
||||
group: wheel
|
||||
remote_src: yes
|
||||
|
||||
- name: Delete local shellpki-dev repo
|
||||
file:
|
||||
state: absent
|
||||
dest: "/root/shellpki-dev"
|
||||
# END TODO
|
||||
|
||||
- name: Install cron to warn about certificates expiration
|
||||
cron:
|
||||
name: "OpenVPN certificates expiration"
|
||||
special_time: monthly
|
||||
job: '/usr/share/scripts/cert-expirations.sh | mail -E -s "PKI VPN {{ ansible_hostname }} : recapitulatif expirations" {{ client_email }}'
|
||||
|
||||
- name: Warn the user about command to execute manually
|
||||
pause:
|
||||
prompt: |
|
||||
/!\ WARNING /!\
|
||||
You have to manually create the CA on the server with "shellpki init {{ ansible_fqdn }}". The command will ask you to create a password, and will ask you again to give the same one several times.
|
||||
You have to manually generate the CRL on the server with "openssl ca -gencrl -keyfile /etc/shellpki/cakey.key -cert /etc/shellpki/cacert.pem -out /etc/shellpki/crl.pem -config /etc/shellpki/openssl.cnf". The previously created password will be asked.
|
||||
You have to manually create the server's certificate with "shellpki create {{ ansible_fqdn }}".
|
||||
You have to adjust the config file "/etc/openvpn/server.conf" for the following parameters : local (to check), cert (to check), key (to add), server (to check), push (to complete if needed).
|
||||
Finally, you can (re)start the OpenVPN service with "rcctl restart openvpn".
|
||||
|
||||
Press enter to exit when it's done.
|
||||
|
|
@ -5,6 +5,7 @@ proto udp
|
|||
|
||||
remote {{ ansible_fqdn }} 1194
|
||||
|
||||
nobind
|
||||
persist-key
|
||||
persist-tun
|
||||
|
||||
|
|
|
@ -7,12 +7,15 @@ proto udp
|
|||
dev tun
|
||||
mode server
|
||||
keepalive 10 120
|
||||
tls-exit
|
||||
|
||||
cipher AES-256-GCM # AES
|
||||
|
||||
persist-key
|
||||
persist-tun
|
||||
|
||||
ifconfig-pool-persist /etc/openvpn/ipp.txt
|
||||
|
||||
status /var/log/openvpn-status.log
|
||||
log-append /var/log/openvpn.log
|
||||
|
||||
|
|
|
@ -8,6 +8,23 @@ fi;
|
|||
|
||||
PHPVersion=$(grep SetHandler /etc/apache2/sites-enabled/$LOGNAME.conf 2>/dev/null | grep -m 1 -o 'fpm[0-9][0-9]' | head -n 1 | sed 's/php//g' | sed 's/fpm//g')
|
||||
|
||||
# If $PHPVersion is empty, look for "SetHandler" in the includes.
|
||||
if [ "$PHPVersion" == "" ]; then
|
||||
includes=$(grep -w "Include" /etc/apache2/sites-enabled/$LOGNAME.conf | uniq | awk '{print $2}')
|
||||
for f in "$includes"; do
|
||||
# Relative path -> absolute
|
||||
if [ ${f:0:1} != "/" ]; then
|
||||
f="/etc/apache2/${f}"
|
||||
fi
|
||||
|
||||
# Grep again
|
||||
PHPVersion=$(grep SetHandler "${f}" 2>/dev/null | grep -m 1 -o 'fpm[0-9][0-9]' | head -n 1 | sed 's/php//g' | sed 's/fpm//g')
|
||||
if [ "$PHPVersion" != "" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$PHPVersion" != "" ]; then
|
||||
lxc-attach -n php$PHPVersion -- su - $LOGNAME -c "cd \"${PWD@E}\" && php ${*@Q}"
|
||||
else
|
||||
|
|
|
@ -38,5 +38,6 @@ dependencies:
|
|||
- { role: evolix/lxc-php, lxc_php_version: php73, lxc_php_create_mysql_link: True, when: "'php73' in packweb_multiphp_versions" }
|
||||
- { role: evolix/lxc-php, lxc_php_version: php74, lxc_php_create_mysql_link: True, when: "'php74' in packweb_multiphp_versions" }
|
||||
- { role: evolix/lxc-php, lxc_php_version: php80, lxc_php_create_mysql_link: True, when: "'php80' in packweb_multiphp_versions" }
|
||||
- { role: evolix/lxc-php, lxc_php_version: php81, lxc_php_create_mysql_link: True, when: "'php81' in packweb_multiphp_versions" }
|
||||
- { role: evolix/webapps/evoadmin-web, evoadmin_enable_vhost: "{{ packweb_enable_evoadmin_vhost }}", evoadmin_multiphp_versions: "{{ packweb_multiphp_versions }}" }
|
||||
- { role: evolix/evoacme }
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
src: postgresql.service.override.conf
|
||||
dest: /etc/systemd/system/postgresql@.service.d/override.conf
|
||||
force: yes
|
||||
mode: "0644"
|
||||
notify:
|
||||
- reload systemd
|
||||
- restart postgresql
|
||||
|
|
|
@ -73,7 +73,7 @@ if systemctl is-enabled -q redis-server; then
|
|||
fi
|
||||
|
||||
# additional instances
|
||||
conf_files=$(ls -1 /etc/redis-*/redis.conf)
|
||||
conf_files=$(ls -1 /etc/redis-*/redis.conf 2> /dev/null)
|
||||
for conf_file in ${conf_files}; do
|
||||
name=$(dirname "${conf_file}" | sed '{s|/etc/redis-||}')
|
||||
if systemctl is-enabled -q "redis-server@${name}.service"; then
|
||||
|
|
|
@ -87,7 +87,6 @@
|
|||
mode: "0755"
|
||||
owner: root
|
||||
group: root
|
||||
when: redis_instance_name is defined
|
||||
tags:
|
||||
- redis
|
||||
- nrpe
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
template:
|
||||
src: systemd-override.conf.j2
|
||||
dest: /etc/systemd/system/squid.service.d/override.conf
|
||||
mode: "0644"
|
||||
force: yes
|
||||
register: _squid_systemd_override
|
||||
|
||||
|
|
|
@ -1,1165 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
# -*- perl -*-
|
||||
#
|
||||
# varnish4_ - Munin plugin to for Varnish 4.x
|
||||
# Copyright (C) 2009 Redpill Linpro AS
|
||||
#
|
||||
# Author: Kristian Lyngstol <kristian@bohemians.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
=head1 NAME
|
||||
|
||||
varnish4_ - Munin plugin to monitor various aspects of varnish
|
||||
|
||||
=head1 APPLICABLE SYSTEMS
|
||||
|
||||
Varnish 4.x with varnishstat
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
The plugin needs to be able to execute varnishstat.
|
||||
|
||||
The configuration section shows the defaults
|
||||
[varnish4_*]
|
||||
env.varnishstat varnishstat
|
||||
env.name
|
||||
|
||||
env.varnishstat can be a full path to varnishstat if it's
|
||||
not in the path already.
|
||||
|
||||
env.name is blank (undefined) by default and can be used to specify a -n
|
||||
name argument to varnish if multiple instances are running on the same
|
||||
server.
|
||||
|
||||
A few aspects are not linked by default. They are marked as
|
||||
'DEBUG' => 'yes' (or any other value). They are:
|
||||
|
||||
vcl, bans, bans_lurker, lru, objects_per_objhead,
|
||||
losthdr, esi, hcb, shm, shm_writes, overflow,
|
||||
session, session_herd, gzip
|
||||
|
||||
You can link them yourself with something like this:
|
||||
|
||||
ln -s @@LIBDIR@@/plugins/varnish4_ \
|
||||
@@CONFDIR@@/plugins/varnish4_data_structures
|
||||
|
||||
=head1 INTERPRETATION
|
||||
|
||||
Each graph uses data from varnishstat.
|
||||
|
||||
=head1 MAGIC MARKERS
|
||||
|
||||
#%# family=auto
|
||||
#%# capabilities=autoconf suggest
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
$Id$
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
The hit_rate graph requires munin r2040 or newer to display
|
||||
correctly.
|
||||
|
||||
=head1 PATCHES-TO
|
||||
|
||||
Please send patches to Kristian Lyngstol <kristian@bohemians.org>
|
||||
and/or varnish-misc@varnish-cache.org for significant changes. Munin SVN
|
||||
is the authoritative repository for this plugin.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Kristian Lyngstol <kristian@bohemians.org>
|
||||
|
||||
=head1 MODIFICATIONS
|
||||
|
||||
Ingo Oppermann <ingo.oppermann@gmail.com>
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
GPLv2
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
use XML::Parser;
|
||||
use strict;
|
||||
|
||||
# Set to 1 to enable output when a variable is defined in a graph but
|
||||
# omitted because it doesn't exist in varnishstat.
|
||||
my $DEBUG = 0;
|
||||
|
||||
# Set to 1 to ignore 'DEBUG' and suggest all available aspects.
|
||||
my $FULL_SUGGEST = 0;
|
||||
|
||||
# Varnishstat executable. Include full path if it's not in your path.
|
||||
my $varnishstatexec = exists $ENV{'varnishstat'} ? $ENV{'varnishstat'} : "varnishstat";
|
||||
|
||||
# For multiple instances
|
||||
my $varnishname = exists $ENV{'name'} ? $ENV{'name'} : undef;
|
||||
|
||||
my $self; # Haha, myself, what a clever pun.
|
||||
|
||||
# Parameters that can be defined on top level of a graph. Config will print
|
||||
# them as "graph_$foo $value\n"
|
||||
my @graph_parameters = ('title','total','order','scale','vlabel','args');
|
||||
|
||||
# Parameters that can be defined on a value-to-value basis and will be
|
||||
# blindly passed to config. Printed as "$fieldname.$param $value\n".
|
||||
#
|
||||
# 'label' is hardcoded as it defaults to a varnishstat-description if not
|
||||
# set.
|
||||
my @field_parameters = ('graph', 'min', 'max', 'draw', 'cdef', 'warning',
|
||||
'colour', 'info', 'type');
|
||||
|
||||
# Varnishstat data is stored here. Example
|
||||
# ('n_vbe' => { 'value' => '124', 'description'=>...,'flag'=>... }, SMA =>
|
||||
# { s0 => { 'value' => '...', 'flag'=> '...' },'Transient' => ...})
|
||||
# Both dynamic and static counters are kept here.
|
||||
#
|
||||
# Notes:
|
||||
# - The 'flag' field for a counter is in RRD-dialect, not varnishstat
|
||||
my %data;
|
||||
|
||||
# Data structure that defines all possible graphs (aspects) and how they
|
||||
# are to be plotted. Every top-level entry is a graph/aspect. Each
|
||||
# top-level graph MUST have title set and 'values'.
|
||||
#
|
||||
# The 'values' hash must have at least one value definition. The actual
|
||||
# value used is either fetched from varnishstat based on the value-name, or
|
||||
# if 'rpn' is defined: calculated. 'type' SHOULD be set.
|
||||
#
|
||||
# Graphs with 'DEBUG' set to anything is omitted from 'suggest'.
|
||||
#
|
||||
# 'rpn' on values allows easy access to graphs consisting of multiple
|
||||
# values from varnishstat. (Reverse polish notation). The RPN
|
||||
# implementation only accepts +-*/ and varnishstat-values.
|
||||
#
|
||||
# With the exception of 'label', which is filled with the
|
||||
# varnishstat-description if left undefined, any value left undefined will
|
||||
# be left up to Munin to define/ignore/yell about.
|
||||
#
|
||||
# For dynamic counters, the values listed need to specify a counter and
|
||||
# family. This will plot the specified counter for each identity within
|
||||
# that family. Example: family of SMA, counter c_fail. This will create a
|
||||
# c_fail-counter for each of the SMA-identities (e.g: Transient, s0, etc).
|
||||
# For dynamic graphs, the value-name is only used to identify the data
|
||||
# point, and does not relate to any varnishstat data as that is set by
|
||||
# family/counter.
|
||||
#
|
||||
# Note that dynamic counters fetch the type from the XML and things like
|
||||
# min/max are currently not supported (and silently ignored).
|
||||
#
|
||||
# See munin documentation or rrdgraph/rrdtool for more information.
|
||||
my %ASPECTS = (
|
||||
'request_rate' => {
|
||||
'title' => 'Request rates',
|
||||
'order' => 'cache_hit cache_hitpass cache_miss '
|
||||
. 'backend_conn backend_unhealthy '
|
||||
. 'client_req client_conn' ,
|
||||
'values' => {
|
||||
'sess_conn' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'colour' => '444444',
|
||||
'graph' => 'ON'
|
||||
},
|
||||
'client_req' => {
|
||||
'type' => 'DERIVE',
|
||||
'colour' => '111111',
|
||||
'min' => '0'
|
||||
},
|
||||
'cache_hit' => {
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'AREA',
|
||||
'colour' => '00FF00',
|
||||
'min' => '0'
|
||||
},
|
||||
'cache_hitpass' => {
|
||||
'info' => 'Hitpass are cached passes: An '
|
||||
. 'entry in the cache instructing '
|
||||
. 'Varnish to pass. Typically '
|
||||
. 'achieved after a pass in '
|
||||
. 'vcl_fetch.',
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'STACK',
|
||||
'colour' => 'FFFF00',
|
||||
'min' => '0'
|
||||
},
|
||||
'cache_miss' => {
|
||||
'type' => 'DERIVE',
|
||||
'colour' => 'FF0000',
|
||||
'draw' => 'STACK',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_conn' => {
|
||||
'type' => 'DERIVE',
|
||||
'colour' => '995599',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_unhealthy' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'colour' => 'FF55FF'
|
||||
},
|
||||
's_pipe' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'colour' => '1d2bdf'
|
||||
},
|
||||
's_pass' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'colour' => '785d0d'
|
||||
}
|
||||
}
|
||||
},
|
||||
'hit_rate' => {
|
||||
'title' => 'Hit rates',
|
||||
'order' => 'client_req cache_hit cache_miss '
|
||||
. 'cache_hitpass' ,
|
||||
'vlabel' => '%',
|
||||
'args' => '-u 100 --rigid',
|
||||
'scale' => 'no',
|
||||
'values' => {
|
||||
'client_req' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'graph' => 'off',
|
||||
'rpn' => [ 'cache_hit' , 'cache_miss' , 'cache_hitpass' , '+' , '+' ]
|
||||
},
|
||||
'cache_hit' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'draw' => 'AREA',
|
||||
'cdef' => 'cache_hit,client_req,/,100,*'
|
||||
},
|
||||
'cache_miss' => {
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'STACK',
|
||||
'min' => '0',
|
||||
'cdef' => 'cache_miss,client_req,/,100,*'
|
||||
},
|
||||
'cache_hitpass' => {
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'STACK',
|
||||
'min' => '0',
|
||||
'cdef' => 'cache_hitpass,client_req,/,100,*'
|
||||
},
|
||||
}
|
||||
},
|
||||
'backend_traffic' => {
|
||||
'title' => 'Backend traffic',
|
||||
'values' => {
|
||||
'backend_conn' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_unhealthy' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'warning' => ':1'
|
||||
},
|
||||
'backend_busy' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_fail' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_reuse' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => 0
|
||||
},
|
||||
'backend_recycle' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => 0
|
||||
},
|
||||
'backend_toolate' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_retry' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_req' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'objects' => {
|
||||
'title' => 'Number of objects',
|
||||
'order' => 'n_object n_objectcore n_vampireobject n_objecthead',
|
||||
'values' => {
|
||||
'n_object' => {
|
||||
'type' => 'GAUGE',
|
||||
'label' => 'Number of objects'
|
||||
},
|
||||
'n_objectcore' => {
|
||||
'type' => 'GAUGE',
|
||||
'label' => 'Number of object cores'
|
||||
},
|
||||
'n_vampireobject' => {
|
||||
'type' => 'GAUGE',
|
||||
'label' => 'Number of unresurrected objects'
|
||||
},
|
||||
'n_objecthead' => {
|
||||
'type' => 'GAUGE',
|
||||
'label' => 'Number of object heads',
|
||||
'info' => 'Each object head can have one '
|
||||
. 'or more object attached, '
|
||||
. 'typically based on the Vary: header'
|
||||
}
|
||||
}
|
||||
},
|
||||
'transfer_rates' => {
|
||||
'title' => 'Transfer rates',
|
||||
'order' => 's_resp_bodybytes s_resp_hdrbytes',
|
||||
'args' => '-l 0',
|
||||
'vlabel' => 'bit/s',
|
||||
'values' => {
|
||||
's_resp_hdrbytes' => {
|
||||
'type' => 'DERIVE',
|
||||
'label' => 'Header traffic',
|
||||
'draw' => 'STACK',
|
||||
'min' => '0',
|
||||
'info' => 'HTTP Header traffic. TCP/IP '
|
||||
. 'overhead is not included.',
|
||||
'cdef' => 's_resp_hdrbytes,8,*'
|
||||
},
|
||||
's_resp_bodybytes' => {
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'AREA',
|
||||
'label' => 'Body traffic',
|
||||
'min' => '0',
|
||||
'cdef' => 's_resp_bodybytes,8,*'
|
||||
}
|
||||
}
|
||||
},
|
||||
'threads' => {
|
||||
'title' => 'Thread status',
|
||||
'values' => {
|
||||
'threads' => {
|
||||
'type' => 'GAUGE',
|
||||
'min' => '0',
|
||||
'warning' => '1:'
|
||||
},
|
||||
'threads_created' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'threads_failed' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'warning' => ':1'
|
||||
},
|
||||
'threads_limited' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'threads_destroyed' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'warning' => ':1'
|
||||
}
|
||||
}
|
||||
},
|
||||
'memory_usage' => {
|
||||
'title' => 'Memory usage',
|
||||
'args' => '--base 1024',
|
||||
'vlabel' => 'bytes',
|
||||
'values' => {
|
||||
'sms_balloc' => {
|
||||
'type' => 'GAUGE',
|
||||
},
|
||||
'sms_nbytes' => {
|
||||
'type' => 'GAUGE',
|
||||
},
|
||||
'SMA_1' => {
|
||||
'counter' => 'g_bytes',
|
||||
'family' => 'SMA',
|
||||
},
|
||||
'SMA_2' => {
|
||||
'counter' => 'g_space',
|
||||
'family' => 'SMA',
|
||||
},
|
||||
'SMA_3' => {
|
||||
'counter' => 'c_bytes',
|
||||
'family' => 'SMA'
|
||||
},
|
||||
'SMF_1' => {
|
||||
'counter' => 'g_bytes',
|
||||
'family' => 'SMF',
|
||||
},
|
||||
'SMF_2' => {
|
||||
'counter' => 'g_space',
|
||||
'family' => 'SMF',
|
||||
}
|
||||
}
|
||||
},
|
||||
'uptime' => {
|
||||
'title' => 'Varnish uptime',
|
||||
'vlabel' => 'days',
|
||||
'scale' => 'no',
|
||||
'values' => {
|
||||
'uptime' => {
|
||||
'type' => 'GAUGE',
|
||||
'cdef' => 'uptime,86400,/'
|
||||
}
|
||||
}
|
||||
},
|
||||
'objects_per_objhead' => {
|
||||
'title' => 'Objects per objecthead',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'obj_per_objhead' => {
|
||||
'type' => 'GAUGE',
|
||||
'label' => 'Objects per object heads',
|
||||
'rpn' => [ 'n_object','n_objecthead','/' ]
|
||||
}
|
||||
}
|
||||
},
|
||||
'losthdr' => {
|
||||
'title' => 'HTTP Header overflows',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'losthdr' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'hcb' => {
|
||||
'title' => 'Critbit data',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'hcb_nolock' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'hcb_lock' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'hcb_insert' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'esi' => {
|
||||
'title' => 'ESI',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'esi_parse' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'esi_errors' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'esi_warnings' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'session' => {
|
||||
'title' => 'Sessions',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'sess_conn' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_drop' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_fail' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_pipe_overflow' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_queued' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_dropped' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_closed' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_pipeline' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_readahead' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'session_herd' => {
|
||||
'title' => 'Session herd',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'sess_herd' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'shm_writes' => {
|
||||
'title' => 'SHM writes and records',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'shm_records' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'shm_writes' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'shm' => {
|
||||
'title' => 'Shared memory activity',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'shm_flushes' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'shm_cont' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'shm_cycles' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'allocations' => {
|
||||
'title' => 'Memory allocation requests',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'sm_nreq' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sma_nreq' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sms_nreq' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'vcl' => {
|
||||
'title' => 'VCL',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'n_backend' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'n_vcl' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'n_vcl_avail' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'n_vcl_discard' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'bans' => {
|
||||
'title' => 'Bans',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'bans' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_added' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_deleted' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_completed' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_obj' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_req' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_tested' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_obj_killed' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_tests_tested' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_dups' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_persisted_bytes' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_persisted_fragmentation' => {
|
||||
'type' => 'GAUGE'
|
||||
}
|
||||
}
|
||||
},
|
||||
'bans_lurker' => {
|
||||
'title' => 'Ban Lurker',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'bans_lurker_tested' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_lurker_tests_tested' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_lurker_obj_killed' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_lurker_contention' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'expunge' => {
|
||||
'title' => 'Object expunging',
|
||||
'order' => 'n_expired n_lru_nuked',
|
||||
'values' => {
|
||||
'n_expired' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'n_lru_nuked' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'lru' => {
|
||||
'title' => 'LRU activity',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'n_lru_nuked' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'n_lru_moved' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'bad' => {
|
||||
'title' => 'Misbehavior',
|
||||
'values' => {
|
||||
'SMA_1' => {
|
||||
'counter' => 'c_fail',
|
||||
'family' => 'SMA',
|
||||
},
|
||||
'SMF_1' => {
|
||||
'counter' => 'c_fail',
|
||||
'family' => 'SMF',
|
||||
},
|
||||
'sess_drop' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'backend_unhealthy' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'fetch_failed' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'backend_busy' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'threads_failed' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'threads_limited' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'threads_destroyed' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'thread_queue_len' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'losthdr' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'esi_errors' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'esi_warnings' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'sess_fail' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'sess_pipe_overflow' => {
|
||||
'type' => 'DERIVE'
|
||||
}
|
||||
}
|
||||
},
|
||||
'gzip' => {
|
||||
'title' => 'GZIP activity',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'n_gzip' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'n_gunzip' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
################################
|
||||
# Various helper functions #
|
||||
################################
|
||||
|
||||
# Translate $_[0] from varnish' internal types (flags) to munin/rrd
|
||||
# variants (e.g: from 'i' to GAUGE). Returns the result.
|
||||
sub translate_type
|
||||
{
|
||||
my $d = $_[0];
|
||||
if ($d eq "i") {
|
||||
$d = "GAUGE";
|
||||
} elsif ($d eq "a") {
|
||||
$d = "DERIVE";
|
||||
}
|
||||
return $d;
|
||||
}
|
||||
|
||||
# Print the value of a two-dimensional hash if it exist.
|
||||
# Returns false if non-existent.
|
||||
#
|
||||
# Output is formatted for plugins if arg4 is blank, otherwise arg4 is used
|
||||
# as the title/name of the field (ie: arg4=graph_title).
|
||||
sub print_if_exist
|
||||
{
|
||||
my %values = %{$_[0]};
|
||||
my $value = $_[1];
|
||||
my $field = $_[2];
|
||||
my $title = "$value.$field";
|
||||
if (defined($_[3])) {
|
||||
$title = $_[3];
|
||||
}
|
||||
if (defined($values{$value}{$field})) {
|
||||
print "$title $values{$value}{$field}\n";
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
# Create a output-friendly name
|
||||
sub normalize_name
|
||||
{
|
||||
my $name = $_[0];
|
||||
$name =~ s/[^a-zA-Z0-9]/_/g;
|
||||
return $name;
|
||||
}
|
||||
|
||||
# Braindead RPN: +,-,/,* will pop two items from @stack, and perform
|
||||
# the relevant operation on the items. If the item in the array isn't one
|
||||
# of the 4 basic math operations, a value from varnishstat is pushed on to
|
||||
# the stack. IE: 'client_req','client_conn','/' will leave the value of
|
||||
# "client_req/client_conn" on the stack.
|
||||
#
|
||||
# If only one item is left on the stack, it is printed. Otherwise, an error
|
||||
# message is printed.
|
||||
sub rpn
|
||||
{
|
||||
my @stack;
|
||||
my $left;
|
||||
my $right;
|
||||
foreach my $item (@{$_[0]}) {
|
||||
if ($item eq "+") {
|
||||
$right = pop(@stack);
|
||||
$left = pop(@stack);
|
||||
push(@stack,$left+$right);
|
||||
} elsif ($item eq "-") {
|
||||
$right = pop(@stack);
|
||||
$left = pop(@stack);
|
||||
push(@stack,$left-$right);
|
||||
} elsif ($item eq "/") {
|
||||
$right = pop(@stack);
|
||||
$left = pop(@stack);
|
||||
push(@stack,$left/$right);
|
||||
} elsif ($item eq "*") {
|
||||
$right = pop(@stack);
|
||||
$left = pop(@stack);
|
||||
push(@stack,$left*$right);
|
||||
} else {
|
||||
push(@stack,int($data{$item}{'value'}));
|
||||
}
|
||||
}
|
||||
if (@stack > 1)
|
||||
{
|
||||
print STDERR "RPN error: Stack has more than one item left.\n";
|
||||
print STDERR "@stack\n";
|
||||
exit 255;
|
||||
}
|
||||
print "@stack";
|
||||
print "\n";
|
||||
}
|
||||
|
||||
# Bail-function.
|
||||
sub usage
|
||||
{
|
||||
if (@_) {
|
||||
print STDERR "@_" . "\n\n";
|
||||
}
|
||||
print STDERR "Known arguments: suggest, config, autoconf.\n";
|
||||
print STDERR "Run with suggest to get a list of known aspects.\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
################################
|
||||
# XML Parsing #
|
||||
################################
|
||||
# The following code is for parsing varnishstat -x. While %data should be
|
||||
# stable, the following bits can easily be replaced with anything (json, an
|
||||
# other xml-parser, magic, etc)
|
||||
#
|
||||
# The basic concept is simple enough. Only worry about stuff inside
|
||||
# <state>. Updating %state on each new data field, and commit it to %data
|
||||
# when </state> is seen.
|
||||
#
|
||||
# We do use translate_type() on the 'flag' field.
|
||||
|
||||
|
||||
# Internal state for the XML parsing
|
||||
my %state = (
|
||||
'stat' => 0, # inside <stat> or not
|
||||
'field' => 'none', # <name>, <value>, <stat>, etc.
|
||||
);
|
||||
|
||||
# Reset the state of XML, mainly used for end-elements.
|
||||
sub xml_reset_state() {
|
||||
$state{'stat'} = '0';
|
||||
$state{'field'} = 'none';
|
||||
$state{'values'} = ();
|
||||
}
|
||||
|
||||
# Callback for data entry. Cleans leading whitespace and updates state.
|
||||
sub xml_characters {
|
||||
my $d = $_[1];
|
||||
if ($state{'stat'} == 0) {
|
||||
return;
|
||||
}
|
||||
if ($state{'field'} eq "type" && $d eq "MAIN") {
|
||||
return;
|
||||
}
|
||||
$d =~ s/^\s*$//g;
|
||||
if ($d eq "") {
|
||||
return;
|
||||
}
|
||||
$state{'values'}{$state{'field'}} = $d;
|
||||
}
|
||||
|
||||
# Store the current state in %data. Issued at </stat>
|
||||
# Note that 'flag' is translated to RRD-equivalents here.
|
||||
sub xml_commit_state
|
||||
{
|
||||
my $name = $state{'values'}{'name'};
|
||||
my $type = $state{'values'}{'type'};
|
||||
my $ident = $state{'values'}{'ident'};
|
||||
|
||||
foreach my $key (keys %{$state{'values'}}) {
|
||||
my $data = $state{'values'}{$key};
|
||||
if ($key eq 'flag') {
|
||||
$data = translate_type($data);
|
||||
}
|
||||
if (defined($type) and $type ne '' and defined($ident) and $ident ne '') {
|
||||
$data{$type}{$ident}{$name}{$key} = $data;
|
||||
} else {
|
||||
$data{$name}{$key} = $data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Callback for end tag. E.g: </stat>
|
||||
sub xml_end_elem {
|
||||
my $element = $_[1];
|
||||
if ($element ne "stat") {
|
||||
return;
|
||||
}
|
||||
|
||||
xml_commit_state();
|
||||
xml_reset_state();
|
||||
}
|
||||
|
||||
# Callback for opening tag. E.g: <stat>
|
||||
sub xml_start_elem {
|
||||
$state{'field'} = $_[1];
|
||||
if ($state{'field'} eq "stat") {
|
||||
$state{'stat'} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
################################
|
||||
# Internal API #
|
||||
################################
|
||||
|
||||
|
||||
# Populate %data, includes both values and descriptions and more.
|
||||
# Currently driven by XML, but that could change.
|
||||
sub populate_stats
|
||||
{
|
||||
my $arg = "-x";
|
||||
my $parser = new XML::Parser(Handlers => {Start => \&xml_start_elem,
|
||||
End => \&xml_end_elem,
|
||||
Char => \&xml_characters} );
|
||||
|
||||
if ($varnishname) {
|
||||
$arg .= " -n $varnishname";
|
||||
}
|
||||
|
||||
open (XMLDATA, "$varnishstatexec $arg|") or die "meh";
|
||||
$parser->parse(*XMLDATA, ProtocolEncoding => 'ISO-8859-1');
|
||||
close(XMLDATA);
|
||||
}
|
||||
|
||||
# Prints the fields in the list in $_[2] (e.g: 'value'/'description') for
|
||||
# each identity of the varnish counter/family combination as defined by
|
||||
# the $_[0]-counter on the aspect definition. Err, that's jibberish, so
|
||||
# an example:
|
||||
#
|
||||
# e.g: dynamic_print('SMA_1','',('value'))
|
||||
# e.g: dynamic_print('SMA_2','.label',('ident','description'))
|
||||
# SMA_1 is the counter-value. If it is a dynamic counter, it has a counter
|
||||
# and family-member (e.g: counter: c_req, family: SMA) and print_dynamic
|
||||
# will print c_req for each SMA-identity.
|
||||
#
|
||||
# Note that the variables to print is a list. This is to allow printing a
|
||||
# single item with multiple fields. Typically for identity+description so
|
||||
# you can distinguish between different data points.
|
||||
#
|
||||
# Returns true if it was a dynamic counter.
|
||||
sub print_dynamic
|
||||
{
|
||||
my $name = $_[0];
|
||||
shift;
|
||||
my $suffix = $_[0];
|
||||
shift;
|
||||
my @field = @_;
|
||||
if (!defined($ASPECTS{$self}{'values'}{$name}{'counter'})) {
|
||||
return 0;
|
||||
}
|
||||
if (!defined($ASPECTS{$self}{'values'}{$name}{'family'})) {
|
||||
return 0;
|
||||
}
|
||||
my $counter = $ASPECTS{$self}{'values'}{$name}{'counter'};
|
||||
my $type = $ASPECTS{$self}{'values'}{$name}{'family'};
|
||||
|
||||
foreach my $key (keys %{$data{$type}}) {
|
||||
my $pname = normalize_name($type . "_" . $key . "_" . $counter);
|
||||
print $pname . $suffix . " ";
|
||||
my $i = 0;
|
||||
foreach my $f (@field) {
|
||||
if ($i != 0) {
|
||||
print " ";
|
||||
}
|
||||
$i += 1;
|
||||
print $data{$type}{$key}{$counter}{$f};
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Read and verify the aspect ($self).
|
||||
sub set_aspect
|
||||
{
|
||||
$self = $0;
|
||||
$self =~ s/^.*\/varnish[0-9]?_//;
|
||||
if (!defined($ASPECTS{$self}) && @ARGV == 0) {
|
||||
usage "No such aspect";
|
||||
}
|
||||
}
|
||||
|
||||
# Print 'yes' if it's reasonable to use this plugin, or 'no' with a
|
||||
# human-readable error message. Always exit true, even if the response
|
||||
# is 'no'.
|
||||
sub autoconf
|
||||
{
|
||||
# XXX: Solaris outputs errors to stderr and always returns true.
|
||||
# XXX: See #873
|
||||
if (`which $varnishstatexec 2>/dev/null` =~ m{^/}) {
|
||||
print "yes\n";
|
||||
} else {
|
||||
print "no ($varnishstatexec could not be found)\n";
|
||||
}
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# Suggest relevant aspects/values of $self.
|
||||
# 'DEBUG'-graphs are excluded.
|
||||
sub suggest
|
||||
{
|
||||
foreach my $key (keys %ASPECTS) {
|
||||
if (defined($ASPECTS{$key}{'DEBUG'}) && $FULL_SUGGEST != 1) {
|
||||
next;
|
||||
}
|
||||
print "$key\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Walk through the relevant aspect and print all top-level configuration
|
||||
# values and value-definitions.
|
||||
sub get_config
|
||||
{
|
||||
my $graph = $_[0];
|
||||
|
||||
# Need to double-check since set_aspect only checks this if there
|
||||
# is no argument (suggest/autoconf doesn't require a valid aspect)
|
||||
if (!defined($ASPECTS{$graph})) {
|
||||
usage "No such aspect";
|
||||
}
|
||||
my %values = %{$ASPECTS{$graph}{'values'}};
|
||||
|
||||
print "graph_category Varnish\n";
|
||||
foreach my $field (@graph_parameters) {
|
||||
print_if_exist(\%ASPECTS,$graph,$field,"graph_$field");
|
||||
}
|
||||
|
||||
foreach my $value (sort keys %values) {
|
||||
# Just print the description/type if it's a dynamic
|
||||
# counter. It'll be silent if it isn't.
|
||||
if(print_dynamic($value,'.label',('description','type','ident'))) {
|
||||
print_dynamic($value,'.type',('flag'));
|
||||
next;
|
||||
}
|
||||
|
||||
# Need either RPN definition or a varnishstat value.
|
||||
if (!defined($data{$value}{'value'}) &&
|
||||
!defined($values{$value}{'rpn'})) {
|
||||
if ($DEBUG) {
|
||||
print "ERROR: $value not part of varnishstat.\n"
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
if (!print_if_exist(\%values,$value,'label')) {
|
||||
print "$value.label $data{$value}{'description'}\n";
|
||||
}
|
||||
foreach my $field (@field_parameters) {
|
||||
print_if_exist(\%values,$value,$field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Handle arguments (config, autoconf, suggest)
|
||||
# Populate stats for config is necessary, but we want to avoid it for
|
||||
# autoconf as it would generate a nasty error.
|
||||
sub check_args
|
||||
{
|
||||
if (@ARGV && $ARGV[0] eq '') {
|
||||
shift @ARGV;
|
||||
}
|
||||
if (@ARGV == 1) {
|
||||
if ($ARGV[0] eq "config") {
|
||||
populate_stats;
|
||||
get_config($self);
|
||||
exit 0;
|
||||
} elsif ($ARGV[0] eq "autoconf") {
|
||||
autoconf($self);
|
||||
exit 0;
|
||||
} elsif ($ARGV[0] eq "suggest") {
|
||||
suggest;
|
||||
exit 0;
|
||||
}
|
||||
usage "Unknown argument";
|
||||
}
|
||||
}
|
||||
|
||||
################################
|
||||
# Execution starts here #
|
||||
################################
|
||||
|
||||
set_aspect;
|
||||
check_args;
|
||||
populate_stats;
|
||||
|
||||
# We only get here if we're supposed to.
|
||||
# Walks through the relevant values and either prints the varnishstat, or
|
||||
# if the 'rpn' variable is set, calls rpn() to execute ... the rpn.
|
||||
#
|
||||
# NOTE: Due to differences in varnish-versions, this checks if the value
|
||||
# actually exist before using it.
|
||||
foreach my $value (sort keys %{$ASPECTS{$self}{'values'}}) {
|
||||
if (defined($ASPECTS{$self}{'values'}{$value}{'rpn'})) {
|
||||
print "$value.value ";
|
||||
rpn($ASPECTS{$self}{'values'}{$value}{'rpn'});
|
||||
} else {
|
||||
if (print_dynamic($value,'.value',('value'))) {
|
||||
next;
|
||||
}
|
||||
|
||||
if (!defined($data{$value}{'value'})) {
|
||||
if ($DEBUG) {
|
||||
print STDERR "Error: $value not part of "
|
||||
. "varnishstat.\n";
|
||||
}
|
||||
next;
|
||||
}
|
||||
print "$value.value ";
|
||||
print "$data{$value}{'value'}\n";
|
||||
}
|
||||
}
|
2
varnish/files/munin/varnish5.conf
Normal file
2
varnish/files/munin/varnish5.conf
Normal file
|
@ -0,0 +1,2 @@
|
|||
[varnish5_*]
|
||||
user vcache
|
1221
varnish/files/munin/varnish5_
Normal file
1221
varnish/files/munin/varnish5_
Normal file
|
@ -0,0 +1,1221 @@
|
|||
#!/usr/bin/perl
|
||||
# -*- perl -*-
|
||||
#
|
||||
# varnish5_ - Munin plugin to for Varnish 5.x and 6.x
|
||||
# Copyright (C) 2009,2018 Redpill Linpro AS
|
||||
#
|
||||
# Author: Kristian Lyngstøl <kristian@bohemians.org>
|
||||
# Pål-Eivind Johnsen <pej@redpill-linpro.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
=head1 NAME
|
||||
|
||||
varnish5_ - Munin plugin to monitor various aspects of varnish
|
||||
|
||||
=head1 APPLICABLE SYSTEMS
|
||||
|
||||
Varnish 5.x with varnishstat
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
The plugin needs to be able to execute varnishstat.
|
||||
|
||||
The configuration section shows the defaults
|
||||
[varnish5_*]
|
||||
group varnish
|
||||
env.varnishstat varnishstat
|
||||
env.name
|
||||
|
||||
env.varnishstat can be a full path to varnishstat if it's
|
||||
not in the path already.
|
||||
|
||||
env.name is blank (undefined) by default and can be used to specify a -n
|
||||
name argument to varnish if multiple instances are running on the same
|
||||
server.
|
||||
|
||||
A few aspects are not linked by default. They are marked as
|
||||
'DEBUG' => 'yes' (or any other value). They are:
|
||||
|
||||
vcl, bans, bans_lurker, lru, objects_per_objhead,
|
||||
losthdr, esi, hcb, shm, shm_writes, overflow,
|
||||
session, session_herd, gzip
|
||||
|
||||
You can link them yourself with something like this:
|
||||
|
||||
ln -s @@LIBDIR@@/plugins/varnish5_ \
|
||||
@@CONFDIR@@/plugins/varnish5_data_structures
|
||||
|
||||
=head1 INTERPRETATION
|
||||
|
||||
Each graph uses data from varnishstat.
|
||||
|
||||
=head1 MAGIC MARKERS
|
||||
|
||||
#%# family=auto
|
||||
#%# capabilities=autoconf suggest
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
$Id$
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
The hit_rate graph requires munin r2040 or newer to display
|
||||
correctly.
|
||||
|
||||
=head1 PATCHES-TO
|
||||
|
||||
Please send patches to Kristian Lyngstøl <kristian@bohemians.org>
|
||||
and/or varnish-misc@varnish-cache.org for significant changes. The
|
||||
munin-contrib Git repo is the authoritative repository for this plugin.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Kristian Lyngstøl <kristian@bohemians.org>
|
||||
|
||||
=head1 MODIFICATIONS
|
||||
|
||||
Ingo Oppermann <ingo.oppermann@gmail.com>
|
||||
Pål-Eivind Johnsen <pej@redpill-linpro.com>
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
GPLv2
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
use XML::Parser;
|
||||
use strict;
|
||||
|
||||
# Set to 1 to enable output when a variable is defined in a graph but
|
||||
# omitted because it doesn't exist in varnishstat.
|
||||
my $DEBUG = 0;
|
||||
|
||||
# Set to 1 to ignore 'DEBUG' and suggest all available aspects.
|
||||
my $FULL_SUGGEST = 0;
|
||||
|
||||
# Varnishstat executable. Include full path if it's not in your path.
|
||||
my $varnishstatexec = exists $ENV{'varnishstat'} ? $ENV{'varnishstat'} : "varnishstat";
|
||||
|
||||
# For multiple instances
|
||||
my $varnishname = exists $ENV{'name'} ? $ENV{'name'} : undef;
|
||||
|
||||
my $self; # Haha, myself, what a clever pun.
|
||||
|
||||
# Parameters that can be defined on top level of a graph. Config will print
|
||||
# them as "graph_$foo $value\n"
|
||||
my @graph_parameters = ('title','total','order','scale','vlabel','args');
|
||||
|
||||
# Parameters that can be defined on a value-to-value basis and will be
|
||||
# blindly passed to config. Printed as "$fieldname.$param $value\n".
|
||||
#
|
||||
# 'label' is hardcoded as it defaults to a varnishstat-description if not
|
||||
# set.
|
||||
my @field_parameters = ('graph', 'min', 'max', 'draw', 'cdef', 'warning',
|
||||
'colour', 'info', 'type');
|
||||
|
||||
# Varnishstat data is stored here. Example
|
||||
# ('n_vbe' => { 'value' => '124', 'description'=>...,'flag'=>... }, SMA =>
|
||||
# { s0 => { 'value' => '...', 'flag'=> '...' },'Transient' => ...})
|
||||
# Both dynamic and static counters are kept here.
|
||||
#
|
||||
# Notes:
|
||||
# - The 'flag' field for a counter is in RRD-dialect, not varnishstat
|
||||
my %data;
|
||||
|
||||
# Data structure that defines all possible graphs (aspects) and how they
|
||||
# are to be plotted. Every top-level entry is a graph/aspect. Each
|
||||
# top-level graph MUST have title set and 'values'.
|
||||
#
|
||||
# The 'values' hash must have at least one value definition. The actual
|
||||
# value used is either fetched from varnishstat based on the value-name, or
|
||||
# if 'rpn' is defined: calculated. 'type' SHOULD be set.
|
||||
#
|
||||
# Graphs with 'DEBUG' set to anything is omitted from 'suggest'.
|
||||
#
|
||||
# 'rpn' on values allows easy access to graphs consisting of multiple
|
||||
# values from varnishstat. (Reverse polish notation). The RPN
|
||||
# implementation only accepts +-*/ and varnishstat-values.
|
||||
#
|
||||
# With the exception of 'label', which is filled with the
|
||||
# varnishstat-description if left undefined, any value left undefined will
|
||||
# be left up to Munin to define/ignore/yell about.
|
||||
#
|
||||
# For dynamic counters, the values listed need to specify a counter and
|
||||
# family. This will plot the specified counter for each identity within
|
||||
# that family. Example: family of SMA, counter c_fail. This will create a
|
||||
# c_fail-counter for each of the SMA-identities (e.g: Transient, s0, etc).
|
||||
# For dynamic graphs, the value-name is only used to identify the data
|
||||
# point, and does not relate to any varnishstat data as that is set by
|
||||
# family/counter.
|
||||
#
|
||||
# Note that dynamic counters fetch the type from the XML and things like
|
||||
# min/max are currently not supported (and silently ignored).
|
||||
#
|
||||
# See munin documentation or rrdgraph/rrdtool for more information.
|
||||
my %ASPECTS = (
|
||||
'request_rate' => {
|
||||
'title' => 'Request rates',
|
||||
'order' => 'cache_hit cache_hitpass cache_miss cache_hitmiss'
|
||||
. 'backend_conn backend_unhealthy '
|
||||
. 'client_req client_conn' ,
|
||||
'values' => {
|
||||
'sess_conn' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'colour' => '444444',
|
||||
'graph' => 'ON'
|
||||
},
|
||||
'client_req' => {
|
||||
'type' => 'DERIVE',
|
||||
'colour' => '111111',
|
||||
'min' => '0'
|
||||
},
|
||||
'cache_hit' => {
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'AREA',
|
||||
'colour' => '00FF00',
|
||||
'min' => '0'
|
||||
},
|
||||
'cache_hitpass' => {
|
||||
'info' => 'Hitpass are cached passes: An '
|
||||
. 'entry in the cache instructing '
|
||||
. 'Varnish to pass. Typically '
|
||||
. 'achieved after a pass in '
|
||||
. 'vcl_fetch.',
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'STACK',
|
||||
'colour' => 'FFFF00',
|
||||
'min' => '0'
|
||||
},
|
||||
'cache_miss' => {
|
||||
'type' => 'DERIVE',
|
||||
'colour' => 'FF0000',
|
||||
'draw' => 'STACK',
|
||||
'min' => '0'
|
||||
},
|
||||
'cache_hitmiss' => {
|
||||
'info' => 'Hitmiss are cached missing: An '
|
||||
. 'entry in the cache instructing '
|
||||
. 'Varnish to pass. Typically '
|
||||
. 'achieved after a pass in '
|
||||
. 'vcl_fetch.',
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'STACK',
|
||||
'colour' => 'FFFF00',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_conn' => {
|
||||
'type' => 'DERIVE',
|
||||
'colour' => '995599',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_unhealthy' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'colour' => 'FF55FF'
|
||||
},
|
||||
's_pipe' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'colour' => '1d2bdf'
|
||||
},
|
||||
's_pass' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'colour' => '785d0d'
|
||||
}
|
||||
}
|
||||
},
|
||||
'hit_rate' => {
|
||||
'title' => 'Hit rates',
|
||||
'order' => 'client_req cache_hit cache_miss '
|
||||
. 'cache_hitpass cache_hitmiss' ,
|
||||
'vlabel' => '%',
|
||||
'args' => '-l 0 -u 100 --rigid',
|
||||
'scale' => 'no',
|
||||
'values' => {
|
||||
'client_req' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'graph' => 'off',
|
||||
'rpn' => [ 'cache_hit' , 'cache_miss' , 'cache_hitpass' ,'cache_hitmiss', '+' , '+' ,'+' ]
|
||||
},
|
||||
'cache_hit' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'draw' => 'AREA',
|
||||
'cdef' => 'cache_hit,client_req,/,100,*'
|
||||
},
|
||||
'cache_miss' => {
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'STACK',
|
||||
'min' => '0',
|
||||
'cdef' => 'cache_miss,client_req,/,100,*'
|
||||
},
|
||||
'cache_hitpass' => {
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'STACK',
|
||||
'min' => '0',
|
||||
'cdef' => 'cache_hitpass,client_req,/,100,*'
|
||||
},
|
||||
'cache_hitmiss' => {
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'STACK',
|
||||
'min' => '0',
|
||||
'cdef' => 'cache_hitmiss,client_req,/,100,*'
|
||||
}
|
||||
}
|
||||
},
|
||||
'backend_traffic' => {
|
||||
'title' => 'Backend traffic',
|
||||
'values' => {
|
||||
'backend_conn' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_unhealthy' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'warning' => ':1'
|
||||
},
|
||||
'backend_busy' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_fail' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_reuse' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => 0
|
||||
},
|
||||
'backend_recycle' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => 0
|
||||
},
|
||||
'backend_retry' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'backend_req' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'objects' => {
|
||||
'title' => 'Number of objects',
|
||||
'order' => 'n_object n_objectcore n_vampireobject n_objecthead',
|
||||
'values' => {
|
||||
'n_object' => {
|
||||
'type' => 'GAUGE',
|
||||
'label' => 'Number of objects'
|
||||
},
|
||||
'n_objectcore' => {
|
||||
'type' => 'GAUGE',
|
||||
'label' => 'Number of object cores'
|
||||
},
|
||||
'n_vampireobject' => {
|
||||
'type' => 'GAUGE',
|
||||
'label' => 'Number of unresurrected objects'
|
||||
},
|
||||
'n_objecthead' => {
|
||||
'type' => 'GAUGE',
|
||||
'label' => 'Number of object heads',
|
||||
'info' => 'Each object head can have one '
|
||||
. 'or more object attached, '
|
||||
. 'typically based on the Vary: header'
|
||||
}
|
||||
}
|
||||
},
|
||||
'transfer_rates' => {
|
||||
'title' => 'Transfer rates',
|
||||
'order' => 's_resp_bodybytes s_resp_hdrbytes',
|
||||
'args' => '-l 0',
|
||||
'vlabel' => 'bit/s',
|
||||
'values' => {
|
||||
's_resp_hdrbytes' => {
|
||||
'type' => 'DERIVE',
|
||||
'label' => 'Header traffic',
|
||||
'draw' => 'STACK',
|
||||
'min' => '0',
|
||||
'info' => 'HTTP Header traffic. TCP/IP '
|
||||
. 'overhead is not included.',
|
||||
'cdef' => 's_resp_hdrbytes,8,*'
|
||||
},
|
||||
's_resp_bodybytes' => {
|
||||
'type' => 'DERIVE',
|
||||
'draw' => 'AREA',
|
||||
'label' => 'Body traffic',
|
||||
'min' => '0',
|
||||
'cdef' => 's_resp_bodybytes,8,*'
|
||||
}
|
||||
}
|
||||
},
|
||||
'threads' => {
|
||||
'title' => 'Thread status',
|
||||
'values' => {
|
||||
'threads' => {
|
||||
'type' => 'GAUGE',
|
||||
'min' => '0',
|
||||
'warning' => '1:'
|
||||
},
|
||||
'threads_created' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'threads_failed' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'warning' => ':1'
|
||||
},
|
||||
'threads_limited' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'threads_destroyed' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0',
|
||||
'warning' => ':1'
|
||||
}
|
||||
}
|
||||
},
|
||||
'memory_usage' => {
|
||||
'title' => 'Memory usage',
|
||||
'args' => '--base 1024',
|
||||
'vlabel' => 'bytes',
|
||||
'values' => {
|
||||
'SMA_1' => {
|
||||
'counter' => 'g_bytes',
|
||||
'family' => 'SMA',
|
||||
},
|
||||
'SMA_2' => {
|
||||
'counter' => 'g_space',
|
||||
'family' => 'SMA',
|
||||
},
|
||||
'SMA_3' => {
|
||||
'counter' => 'c_bytes',
|
||||
'family' => 'SMA'
|
||||
},
|
||||
'SMF_1' => {
|
||||
'counter' => 'g_bytes',
|
||||
'family' => 'SMF',
|
||||
},
|
||||
'SMF_2' => {
|
||||
'counter' => 'g_space',
|
||||
'family' => 'SMF',
|
||||
},
|
||||
'SMF_3' => {
|
||||
'counter' => 'c_bytes',
|
||||
'family' => 'SMF',
|
||||
}
|
||||
}
|
||||
},
|
||||
'main_uptime' => {
|
||||
'type' => 'MAIN',
|
||||
'title' => 'Varnish Child uptime',
|
||||
'vlabel' => 'days',
|
||||
'scale' => 'no',
|
||||
'values' => {
|
||||
'uptime' => {
|
||||
'type' => 'GAUGE',
|
||||
'cdef' => 'uptime,86400,/'
|
||||
},
|
||||
}
|
||||
},
|
||||
'mgt_uptime' => {
|
||||
'type' => 'MGT',
|
||||
'title' => 'Varnish Management uptime',
|
||||
'vlabel' => 'days',
|
||||
'scale' => 'no',
|
||||
'values' => {
|
||||
'uptime' => {
|
||||
'type' => 'GAUGE',
|
||||
'cdef' => 'uptime,86400,/'
|
||||
},
|
||||
}
|
||||
},
|
||||
'objects_per_objhead' => {
|
||||
'title' => 'Objects per objecthead',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'n_objecthead' => {
|
||||
'type' => 'GAUGE',
|
||||
'label' => 'Objects per object heads',
|
||||
'rpn' => [ 'n_object','n_objecthead','/' ]
|
||||
}
|
||||
}
|
||||
},
|
||||
'losthdr' => {
|
||||
'title' => 'HTTP Header overflows',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'losthdr' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'hcb' => {
|
||||
'title' => 'Critbit data',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'hcb_nolock' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'hcb_lock' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'hcb_insert' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'esi' => {
|
||||
'title' => 'ESI',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'esi_errors' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'esi_warnings' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'session' => {
|
||||
'title' => 'Sessions',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'sess_conn' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_drop' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_fail' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_pipe_overflow' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_queued' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_dropped' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_closed' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_pipeline' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sess_readahead' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'session_herd' => {
|
||||
'title' => 'Session herd',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'sess_herd' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'shm_writes' => {
|
||||
'title' => 'SHM writes and records',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'shm_records' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'shm_writes' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'shm' => {
|
||||
'title' => 'Shared memory activity',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'shm_flushes' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'shm_cont' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'shm_cycles' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'allocations' => {
|
||||
'title' => 'Memory allocation requests',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'sm_nreq' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sma_nreq' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'sms_nreq' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'vcl' => {
|
||||
'title' => 'VCL',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'n_backend' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'n_vcl' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'n_vcl_avail' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'n_vcl_discard' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'bans' => {
|
||||
'title' => 'Bans',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'bans' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_added' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_deleted' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_completed' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_obj' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_req' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_tested' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_obj_killed' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_tests_tested' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_dups' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_persisted_bytes' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'bans_persisted_fragmentation' => {
|
||||
'type' => 'GAUGE'
|
||||
}
|
||||
}
|
||||
},
|
||||
'bans_lurker' => {
|
||||
'title' => 'Ban Lurker',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'bans_lurker_tested' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_lurker_tests_tested' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_lurker_obj_killed' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'bans_lurker_contention' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'expunge' => {
|
||||
'title' => 'Object expunging',
|
||||
'order' => 'n_expired n_lru_nuked',
|
||||
'values' => {
|
||||
'n_expired' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'n_lru_nuked' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'lru' => {
|
||||
'title' => 'LRU activity',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'n_lru_nuked' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'n_lru_moved' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'bad' => {
|
||||
'title' => 'Misbehavior',
|
||||
'values' => {
|
||||
'SMA_1' => {
|
||||
'counter' => 'c_fail',
|
||||
'family' => 'SMA',
|
||||
},
|
||||
'SMF_1' => {
|
||||
'counter' => 'c_fail',
|
||||
'family' => 'SMF',
|
||||
},
|
||||
'sess_drop' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'backend_unhealthy' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'fetch_failed' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'backend_busy' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'threads_failed' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'threads_limited' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'threads_destroyed' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'thread_queue_len' => {
|
||||
'type' => 'GAUGE'
|
||||
},
|
||||
'losthdr' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'esi_errors' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'esi_warnings' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'sess_fail' => {
|
||||
'type' => 'DERIVE'
|
||||
},
|
||||
'sess_pipe_overflow' => {
|
||||
'type' => 'DERIVE'
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
'gzip' => {
|
||||
'title' => 'GZIP activity',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'n_gzip' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
},
|
||||
'n_gunzip' => {
|
||||
'type' => 'DERIVE',
|
||||
'min' => '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
'backend' => {
|
||||
'title' => 'Backend Status',
|
||||
'DEBUG' => 'yes',
|
||||
'values' => {
|
||||
'VBE.boot_1' => {
|
||||
'counter' => 'happy',
|
||||
'family' => 'VBE',
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
################################
|
||||
# Various helper functions #
|
||||
################################
|
||||
|
||||
# Translate $_[0] from varnish' internal types (flags) to munin/rrd
|
||||
# variants (e.g: from 'i' to GAUGE). Returns the result.
|
||||
sub translate_type
|
||||
{
|
||||
my $d = $_[0];
|
||||
if ($d eq "i" or $d eq "g") {
|
||||
$d = "GAUGE";
|
||||
} elsif ($d eq "a" or $d eq "c") {
|
||||
$d = "DERIVE";
|
||||
}
|
||||
return $d;
|
||||
}
|
||||
|
||||
# Print the value of a two-dimensional hash if it exist.
|
||||
# Returns false if non-existent.
|
||||
#
|
||||
# Output is formatted for plugins if arg4 is blank, otherwise arg4 is used
|
||||
# as the title/name of the field (ie: arg4=graph_title).
|
||||
sub print_if_exist
|
||||
{
|
||||
my %values = %{$_[0]};
|
||||
my $value = $_[1];
|
||||
my $field = $_[2];
|
||||
my $pvalue = normalize_name($value);
|
||||
|
||||
my $title = "$pvalue.$field";
|
||||
if (defined($_[3])) {
|
||||
$title = $_[3];
|
||||
}
|
||||
if (defined($values{$value}{$field})) {
|
||||
print "$title $values{$value}{$field}\n";
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
# Create a output-friendly name
|
||||
sub normalize_name
|
||||
{
|
||||
my $name = $_[0];
|
||||
$name =~ s/[^a-zA-Z0-9]/_/g;
|
||||
return $name;
|
||||
}
|
||||
|
||||
# Braindead RPN: +,-,/,* will pop two items from @stack, and perform
|
||||
# the relevant operation on the items. If the item in the array isn't one
|
||||
# of the 4 basic math operations, a value from varnishstat is pushed on to
|
||||
# the stack. IE: 'client_req','client_conn','/' will leave the value of
|
||||
# "client_req/client_conn" on the stack.
|
||||
#
|
||||
# If only one item is left on the stack, it is printed. Otherwise, an error
|
||||
# message is printed.
|
||||
sub rpn
|
||||
{
|
||||
my @stack;
|
||||
my $left;
|
||||
my $right;
|
||||
foreach my $item (@{$_[0]}) {
|
||||
if ($item eq "+") {
|
||||
$right = pop(@stack);
|
||||
$left = pop(@stack);
|
||||
push(@stack,$left+$right);
|
||||
} elsif ($item eq "-") {
|
||||
$right = pop(@stack);
|
||||
$left = pop(@stack);
|
||||
push(@stack,$left-$right);
|
||||
} elsif ($item eq "/") {
|
||||
$right = pop(@stack);
|
||||
$left = pop(@stack);
|
||||
push(@stack,$left/$right);
|
||||
} elsif ($item eq "*") {
|
||||
$right = pop(@stack);
|
||||
$left = pop(@stack);
|
||||
push(@stack,$left*$right);
|
||||
} else {
|
||||
push(@stack,int($data{$item}{'value'}));
|
||||
}
|
||||
}
|
||||
if (@stack > 1)
|
||||
{
|
||||
print STDERR "RPN error: Stack has more than one item left.\n";
|
||||
print STDERR "@stack\n";
|
||||
exit 255;
|
||||
}
|
||||
print "@stack";
|
||||
print "\n";
|
||||
}
|
||||
|
||||
# Bail-function.
|
||||
sub usage
|
||||
{
|
||||
if (@_) {
|
||||
print STDERR "@_" . "\n\n";
|
||||
}
|
||||
print STDERR "Known arguments: suggest, config, autoconf.\n";
|
||||
print STDERR "Run with suggest to get a list of known aspects.\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
################################
|
||||
# XML Parsing #
|
||||
################################
|
||||
# The following code is for parsing varnishstat -x. While %data should be
|
||||
# stable, the following bits can easily be replaced with anything (json, an
|
||||
# other xml-parser, magic, etc)
|
||||
#
|
||||
# The basic concept is simple enough. Only worry about stuff inside
|
||||
# <state>. Updating %state on each new data field, and commit it to %data
|
||||
# when </state> is seen.
|
||||
#
|
||||
# We do use translate_type() on the 'flag' field.
|
||||
|
||||
|
||||
# Internal state for the XML parsing
|
||||
my %state = (
|
||||
'stat' => 0, # inside <stat> or not
|
||||
'field' => 'none', # <name>, <value>, <stat>, etc.
|
||||
);
|
||||
|
||||
# Reset the state of XML, mainly used for end-elements.
|
||||
sub xml_reset_state() {
|
||||
$state{'stat'} = '0';
|
||||
$state{'field'} = 'none';
|
||||
$state{'values'} = ();
|
||||
}
|
||||
|
||||
# Callback for data entry. Cleans leading whitespace and updates state.
|
||||
sub xml_characters {
|
||||
my $d = $_[1];
|
||||
if ($state{'stat'} == 0) {
|
||||
return;
|
||||
}
|
||||
if ($state{'field'} eq "type" && $d eq "MAIN") {
|
||||
return;
|
||||
}
|
||||
$d =~ s/^\s*$//g;
|
||||
if ($d eq "") {
|
||||
return;
|
||||
}
|
||||
$state{'values'}{$state{'field'}} = $d;
|
||||
}
|
||||
|
||||
# Store the current state in %data. Issued at </stat>
|
||||
# Note that 'flag' is translated to RRD-equivalents here.
|
||||
sub xml_commit_state
|
||||
{
|
||||
my $configtype = $ASPECTS{$self}{'type'};
|
||||
my $name = $state{'values'}{'name'};
|
||||
my @namelist = split(/\./,$name);
|
||||
my $type = shift @namelist;
|
||||
if ($configtype eq '' || $configtype eq $type) {
|
||||
my $name = "";
|
||||
my $ident = "";
|
||||
if (scalar(@namelist) == 1) {
|
||||
$name = shift @namelist;
|
||||
} elsif (scalar(@namelist) == 2) {
|
||||
$ident = shift @namelist;
|
||||
$name = shift @namelist;
|
||||
} else {
|
||||
$name = pop @namelist;
|
||||
$ident = join('_', @namelist);
|
||||
}
|
||||
foreach my $key (keys %{$state{'values'}}) {
|
||||
my $data = $state{'values'}{$key};
|
||||
if ($key eq 'flag') {
|
||||
$data = translate_type($data);
|
||||
}
|
||||
if (defined($type) and $type ne '' and defined($ident) and $ident ne '') {
|
||||
$data{$type}{$ident}{$name}{$key} = $data;
|
||||
} else {
|
||||
$data{$name}{$key} = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Callback for end tag. E.g: </stat>
|
||||
sub xml_end_elem {
|
||||
my $element = $_[1];
|
||||
if ($element ne "stat") {
|
||||
return;
|
||||
}
|
||||
|
||||
xml_commit_state();
|
||||
xml_reset_state();
|
||||
}
|
||||
|
||||
# Callback for opening tag. E.g: <stat>
|
||||
sub xml_start_elem {
|
||||
$state{'field'} = $_[1];
|
||||
if ($state{'field'} eq "stat") {
|
||||
$state{'stat'} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
################################
|
||||
# Internal API #
|
||||
################################
|
||||
|
||||
|
||||
# Populate %data, includes both values and descriptions and more.
|
||||
# Currently driven by XML, but that could change.
|
||||
sub populate_stats
|
||||
{
|
||||
my $arg = "-x";
|
||||
my $parser = new XML::Parser(Handlers => {Start => \&xml_start_elem,
|
||||
End => \&xml_end_elem,
|
||||
Char => \&xml_characters} );
|
||||
|
||||
if ($varnishname) {
|
||||
$arg .= " -n $varnishname";
|
||||
}
|
||||
|
||||
open (XMLDATA, "$varnishstatexec $arg|") or die "meh";
|
||||
$parser->parse(*XMLDATA, ProtocolEncoding => 'ISO-8859-1');
|
||||
close(XMLDATA);
|
||||
}
|
||||
|
||||
# Prints the fields in the list in $_[2] (e.g: 'value'/'description') for
|
||||
# each identity of the varnish counter/family combination as defined by
|
||||
# the $_[0]-counter on the aspect definition. Err, that's jibberish, so
|
||||
# an example:
|
||||
#
|
||||
# e.g: dynamic_print('SMA_1','',('value'))
|
||||
# e.g: dynamic_print('SMA_2','.label',('ident','description'))
|
||||
# SMA_1 is the counter-value. If it is a dynamic counter, it has a counter
|
||||
# and family-member (e.g: counter: c_req, family: SMA) and print_dynamic
|
||||
# will print c_req for each SMA-identity.
|
||||
#
|
||||
# Note that the variables to print is a list. This is to allow printing a
|
||||
# single item with multiple fields. Typically for identity+description so
|
||||
# you can distinguish between different data points.
|
||||
#
|
||||
# Returns true if it was a dynamic counter.
|
||||
sub print_dynamic
|
||||
{
|
||||
my $name = $_[0];
|
||||
shift;
|
||||
my $suffix = $_[0];
|
||||
shift;
|
||||
my @field = @_;
|
||||
if (!defined($ASPECTS{$self}{'values'}{$name}{'counter'})) {
|
||||
return 0;
|
||||
}
|
||||
if (!defined($ASPECTS{$self}{'values'}{$name}{'family'})) {
|
||||
return 0;
|
||||
}
|
||||
my $counter = $ASPECTS{$self}{'values'}{$name}{'counter'};
|
||||
my $type = $ASPECTS{$self}{'values'}{$name}{'family'};
|
||||
|
||||
foreach my $key (keys %{$data{$type}}) {
|
||||
my $pname = normalize_name($type . "_" . $key . "_" . $counter);
|
||||
print $pname . $suffix . " ";
|
||||
my $i = 0;
|
||||
foreach my $f (@field) {
|
||||
if ($i != 0) {
|
||||
print " ";
|
||||
}
|
||||
$i += 1;
|
||||
print $data{$type}{$key}{$counter}{$f};
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Read and verify the aspect ($self).
|
||||
sub set_aspect
|
||||
{
|
||||
$self = $0;
|
||||
$self =~ s/^.*\/varnish[0-9]?_//;
|
||||
return if defined($ASPECTS{$self});
|
||||
# remove instance name and try again
|
||||
$self =~ s/^.*?_//;
|
||||
if (!defined($ASPECTS{$self}) && @ARGV == 0) {
|
||||
usage "No such aspect";
|
||||
}
|
||||
}
|
||||
|
||||
# Print 'yes' if it's reasonable to use this plugin, or 'no' with a
|
||||
# human-readable error message. Always exit true, even if the response
|
||||
# is 'no'.
|
||||
sub autoconf
|
||||
{
|
||||
# XXX: Solaris outputs errors to stderr and always returns true.
|
||||
# XXX: See #873
|
||||
if (`which $varnishstatexec 2>/dev/null` =~ m{^/}) {
|
||||
print "yes\n";
|
||||
} else {
|
||||
print "no ($varnishstatexec could not be found)\n";
|
||||
}
|
||||
exit 0;
|
||||
}
|
||||
|
||||
# Suggest relevant aspects/values of $self.
|
||||
# 'DEBUG'-graphs are excluded.
|
||||
sub suggest
|
||||
{
|
||||
foreach my $key (keys %ASPECTS) {
|
||||
if (defined($ASPECTS{$key}{'DEBUG'}) && $FULL_SUGGEST != 1) {
|
||||
next;
|
||||
}
|
||||
print "$key\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Walk through the relevant aspect and print all top-level configuration
|
||||
# values and value-definitions.
|
||||
sub get_config
|
||||
{
|
||||
my $graph = $_[0];
|
||||
|
||||
# Need to double-check since set_aspect only checks this if there
|
||||
# is no argument (suggest/autoconf doesn't require a valid aspect)
|
||||
if (!defined($ASPECTS{$graph})) {
|
||||
usage "No such aspect";
|
||||
}
|
||||
my %values = %{$ASPECTS{$graph}{'values'}};
|
||||
|
||||
print "graph_category varnish\n";
|
||||
foreach my $field (@graph_parameters) {
|
||||
print_if_exist(\%ASPECTS,$graph,$field,"graph_$field");
|
||||
}
|
||||
|
||||
foreach my $value (sort keys %values) {
|
||||
# Just print the description/type if it's a dynamic
|
||||
# counter. It'll be silent if it isn't.
|
||||
if(print_dynamic($value,'.label',('description','type','ident'))) {
|
||||
print_dynamic($value,'.type',('flag'));
|
||||
next;
|
||||
}
|
||||
|
||||
# Need either RPN definition or a varnishstat value.
|
||||
if (!defined($data{$value}{'value'}) &&
|
||||
!defined($values{$value}{'rpn'})) {
|
||||
if ($DEBUG) {
|
||||
print STDERR "ERROR: $value not part of varnishstat.\n"
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
if (!print_if_exist(\%values,$value,'label')) {
|
||||
my $pvalue = normalize_name($value);
|
||||
print "$pvalue.label $data{$value}{'description'}\n";
|
||||
}
|
||||
foreach my $field (@field_parameters) {
|
||||
print_if_exist(\%values,$value,$field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Handle arguments (config, autoconf, suggest)
|
||||
# Populate stats for config is necessary, but we want to avoid it for
|
||||
# autoconf as it would generate a nasty error.
|
||||
sub check_args
|
||||
{
|
||||
if (@ARGV && $ARGV[0] eq '') {
|
||||
shift @ARGV;
|
||||
}
|
||||
if (@ARGV == 1) {
|
||||
if ($ARGV[0] eq "config") {
|
||||
populate_stats;
|
||||
get_config($self);
|
||||
exit 0;
|
||||
} elsif ($ARGV[0] eq "autoconf") {
|
||||
autoconf($self);
|
||||
exit 0;
|
||||
} elsif ($ARGV[0] eq "suggest") {
|
||||
suggest;
|
||||
exit 0;
|
||||
}
|
||||
usage "Unknown argument";
|
||||
}
|
||||
}
|
||||
|
||||
################################
|
||||
# Execution starts here #
|
||||
################################
|
||||
|
||||
set_aspect;
|
||||
check_args;
|
||||
populate_stats;
|
||||
|
||||
# We only get here if we're supposed to.
|
||||
# Walks through the relevant values and either prints the varnishstat, or
|
||||
# if the 'rpn' variable is set, calls rpn() to execute ... the rpn.
|
||||
#
|
||||
# NOTE: Due to differences in varnish-versions, this checks if the value
|
||||
# actually exist before using it.
|
||||
foreach my $value (keys %{$ASPECTS{$self}{'values'}}) {
|
||||
my $pvalue = normalize_name($value);
|
||||
if (defined($ASPECTS{$self}{'values'}{$value}{'rpn'})) {
|
||||
print "$pvalue.value ";
|
||||
rpn($ASPECTS{$self}{'values'}{$value}{'rpn'});
|
||||
} else {
|
||||
if (print_dynamic($value,'.value',('value'))) {
|
||||
next;
|
||||
}
|
||||
|
||||
if (!defined($data{$value}{'value'})) {
|
||||
if ($DEBUG) {
|
||||
print STDERR "Error: $value not part of "
|
||||
. "varnishstat.\n";
|
||||
}
|
||||
next;
|
||||
}
|
||||
print "$pvalue.value ";
|
||||
print "$data{$value}{'value'}\n";
|
||||
}
|
||||
}
|
|
@ -22,29 +22,38 @@
|
|||
mode: "0755"
|
||||
tags: varnish
|
||||
|
||||
- name: Copy varnish4 munin plugin
|
||||
- name: Copy varnish5 munin plugin
|
||||
copy:
|
||||
src: munin/varnish4_
|
||||
src: munin/varnish5_
|
||||
dest: /usr/local/share/munin/plugins/
|
||||
mode: "0755"
|
||||
notify: restart munin-node
|
||||
tags: varnish
|
||||
|
||||
- name: Enable varnish4 munin plugin
|
||||
- name: Enable varnish5 munin plugin
|
||||
file:
|
||||
src: /usr/local/share/munin/plugins/varnish4_
|
||||
dest: "/etc/munin/plugins/varnish4_{{item}}"
|
||||
src: /usr/local/share/munin/plugins/varnish5_
|
||||
dest: "/etc/munin/plugins/varnish5_{{item}}"
|
||||
state: link
|
||||
loop:
|
||||
- backend_traffic
|
||||
- bad
|
||||
- expunge
|
||||
- hit_rate
|
||||
- memory_usage
|
||||
- expunge
|
||||
- objects
|
||||
- request_rate
|
||||
- mgt_uptime
|
||||
- threads
|
||||
- backend_traffic
|
||||
- hit_rate
|
||||
- main_uptime
|
||||
- transfer_rates
|
||||
- uptime
|
||||
- bad
|
||||
notify: restart munin-node
|
||||
tags: varnish
|
||||
|
||||
- name: Copy varnish5 munin plugin config
|
||||
copy:
|
||||
src: munin/varnish5.conf
|
||||
dest: /etc/munin/plugin-conf.d/varnish5
|
||||
mode: "0644"
|
||||
notify: restart munin-node
|
||||
tags: varnish
|
||||
|
|
Loading…
Reference in a new issue