From 91a1b41e564f17ce205a5122cb032ca7b8461493 Mon Sep 17 00:00:00 2001 From: Gardouille Date: Tue, 18 Feb 2020 14:24:31 +0100 Subject: [PATCH] Add smart plugin --- CHANGELOG.md | 3 +- README.md | 9 +- defaults/main.yml | 11 ++ tasks/main.yml | 36 ++++ .../etc/xymon/clientlaunch.d/smart.cfg.j2 | 6 + templates/usr/lib/xymon/client/ext/smart.j2 | 182 ++++++++++++++++++ 6 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 templates/etc/xymon/clientlaunch.d/smart.cfg.j2 create mode 100755 templates/usr/lib/xymon/client/ext/smart.j2 diff --git a/CHANGELOG.md b/CHANGELOG.md index f55bd13..1744fd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ -## v1.4.X +## v1.4.0 ### Enhancements * Add variables to manage plugin interval. * Add netstats plugin managment. +* Add smart plugin from https://github.com/skazi0/xymon-plugins ### Fix * Don't remove any plugin dependencies cause some plugins might have the same. diff --git a/README.md b/README.md index 2e21830..bab7c29 100644 --- a/README.md +++ b/README.md @@ -56,10 +56,17 @@ Manage Xymon (client) installation and configuration. * **xymon_plug_mq_tpl** : Template used to generate the previous config file [default : `etc/xymon/clientlaunch.d/mq.cfg.j2`]. * **xymon_plug_mq_interval** : Time between each run of the `mq` plugin [default : `5m`]. * **xymon_cli__plug_netstats_state** : The state of plugin `netstats` [default : `false`]. -* **xymon_cli__plug_netstats_state** : The packages to install to provide `netstats` plugin [default : `[ 'libfile-which-perl', 'libfile-slurp-perl', 'libipc-run-perl', 'libyaml-tiny-perl', 'iproute2', 'ethtool' ]`]. +* **xymon_cli__plug_netstats_package** : The packages to install to provide `netstats` plugin [default : `[ 'libfile-which-perl', 'libfile-slurp-perl', 'libipc-run-perl', 'libyaml-tiny-perl', 'iproute2', 'ethtool' ]`]. * **xymon_cli__plug_netstats_path** : Configuration file for the `netstats` plugin [default : `/etc/xymon/clientlaunch.d/netstats.cfg`]. * **xymon_cli__plug_netstats_tpl** : Template used to generate the previous config file [default : `etc/xymon/clientlaunch.d/netstats.cfg.j2`]. * **xymon_cli__plug_netstats_interval** : Time between each run of the `netstats` plugin [default : `5m`]. +* **xymon_cli__plug_smart_state** : The state of plugin `smart` [default : `False`]. +* **xymon_cli__plug_smart_package** : The packages to install to provide `smart` plugin [default : `[ 'smartmontools' ]`]. +* **xymon_cli__plug_smart_script_path** : Path to the `smart` script [default : `'/usr/lib/xymon/client/ext/smart'`]. +* **xymon_cli__plug_smart_script_tpl** : Template used to generate the previous script [default : `'usr/lib/xymon/client/ext/smart.j2'`]. +* **xymon_cli__plug_smart_path** : Configuration file for the `smart` plugin [default : `'/etc/xymon/clientlaunch.d/smart.cfg'`]. +* **xymon_cli__plug_smart_tpl** : Template used to generate the previous config file [default : `'etc/xymon/clientlaunch.d/smart.cfg.j2'`]. +* **xymon_cli__plug_smart_interval** : Time between each run of the `smart` plugin [default : `'10m'`] * **xymon_cli__plug_zfs_state** : The state of plugin `zfs` [default : `false`]. * **xymon_cli__plug_zfs_script_path** : Path to the ZFS script [default : `/usr/lib/xymon/client/ext/zfs`]. * **xymon_cli__plug_zfs_script_tpl** : Template used to generate the previous script [default : `usr/lib/xymon/client/ext/zfs.j2`]. diff --git a/defaults/main.yml b/defaults/main.yml index 829c7a2..bd87ba0 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -106,6 +106,17 @@ xymon_cli__plug_netstats_path: '/etc/xymon/clientlaunch.d/netstats.cfg' xymon_cli__plug_netstats_tpl: 'etc/xymon/clientlaunch.d/netstats.cfg.j2' xymon_cli__plug_netstats_interval: '5m' ## ]]] +## Plugin smart [[[ +### From https://github.com/skazi0/xymon-plugins +xymon_cli__plug_smart_state: False +xymon_cli__plug_smart_package: [ 'smartmontools' ] +xymon_cli__plug_smart_script_path: '/usr/lib/xymon/client/ext/smart' +xymon_cli__plug_smart_script_tpl: 'usr/lib/xymon/client/ext/smart.j2' +xymon_cli__plug_smart_path: '/etc/xymon/clientlaunch.d/smart.cfg' +xymon_cli__plug_smart_tpl: 'etc/xymon/clientlaunch.d/smart.cfg.j2' +xymon_cli__plug_smart_interval: '10m' + + # ]]] ## Plugin zfs [[[ xymon_cli__plug_zfs_state: false xymon_cli__plug_zfs_script_path: '/usr/lib/xymon/client/ext/zfs' diff --git a/tasks/main.yml b/tasks/main.yml index 21eacdd..74cc883 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -197,6 +197,42 @@ xymon_plug_manage|bool) notify: restart xymon-client service +# Manage smart plugin [[[1 +- name: PLUGIN smart packages + package: + name: '{{ item }}' + state: 'present' + with_items: + - '{{ xymon_cli__plug_smart_package | to_nice_json }}' + register: smart_plug_result + until: smart_plug_result is success + when: (xymon_cli_manage|bool and + xymon_plug_manage|bool and + xymon_cli__plug_smart_state|bool) + notify: restart xymon-client service + +- name: PLUGIN smart config + template: + src: '{{ xymon_cli__plug_smart_tpl }}' + dest: '{{ xymon_cli__plug_smart_path }}' + owner: root + group: root + mode: 0644 + when: (xymon_cli_manage|bool and + xymon_plug_manage|bool) + notify: restart xymon-client service + +- name: PLUGIN smart script file + template: + src: '{{ xymon_cli__plug_smart_script_tpl }}' + dest: '{{ xymon_cli__plug_smart_script_path }}' + owner: root + group: xymon + mode: 0755 + when: (xymon_cli_manage|bool and + xymon_plug_manage|bool) + notify: restart xymon-client service + # Manage zfs plugin [[[1 - name: PLUGIN zfs config file template: diff --git a/templates/etc/xymon/clientlaunch.d/smart.cfg.j2 b/templates/etc/xymon/clientlaunch.d/smart.cfg.j2 new file mode 100644 index 0000000..465043b --- /dev/null +++ b/templates/etc/xymon/clientlaunch.d/smart.cfg.j2 @@ -0,0 +1,6 @@ +[smart] + {{ '#DISABLED' if xymon_cli__plug_smart_state else 'DISABLED' }} + ENVFILE /etc/xymon/xymonclient.cfg + CMD /usr/bin/sudo -E -u root $XYMONCLIENTHOME/ext/smart + LOGFILE /var/log/xymon/smart.log + INTERVAL {{ xymon_cli__plug_smart_interval }} diff --git a/templates/usr/lib/xymon/client/ext/smart.j2 b/templates/usr/lib/xymon/client/ext/smart.j2 new file mode 100755 index 0000000..16fc245 --- /dev/null +++ b/templates/usr/lib/xymon/client/ext/smart.j2 @@ -0,0 +1,182 @@ +#!/usr/bin/perl +# $Id: sensors 70 2011-11-25 09:21:18Z skazi $ +# Author: Jacek Tomasiak + +# {{ ansible_managed }} +# From ipr-cnrs.xymon role +# Thanks to Jacek Tomasiak − skazi0 +# https://github.com/skazi0/xymon-plugins/blob/master/client/ext/smart + +use strict; +# add script's directory to module search path for Hobbit.pm on non-debian systems +use FindBin; +use lib $FindBin::Bin; + +use Hobbit; +use Data::Dumper; + +my $bb = new Hobbit('smart'); + +my @disks = (); +my %olderr = {}; + +my $CACHETIME = 10; # minutes +my $CACHEFILE = "$ENV{'XYMONTMP'}/$ENV{'MACHINEDOTS'}.smart.cache"; + +&load_config("$ENV{'XYMONTMP'}/logfetch.$ENV{'MACHINEDOTS'}.cfg"); + +# fallback to disk detection if nothing defined in the config +unless (@disks) { + opendir(DIR, '/dev') or die $!; + while (my $dev = readdir(DIR)) { + push(@disks, "/dev/$dev") if ($dev =~ /^[vs]d.*\D$/); + } + closedir(DIR); +} + +my @stat = stat($CACHEFILE); +my $mtime = scalar @stat ? $stat[9] : 0; +# regenerate sensors cache if outdated +if (time() - $mtime > $CACHETIME * 60) +{ + open(OUT, ">$CACHEFILE") or die "cannot open $CACHEFILE"; + + foreach my $name (@disks) + { + print OUT ('=' x 20) . " $name " . ('=' x 20) . "\n"; + my @output = `sudo smartctl -AHi -l error -l selftest $name 2>&1` or die; + my $ncv = ''; + my $newerr = 1; + my $ponhours = undef; + my $lasttest = undef; + foreach my $line (@output) + { + # skip header + next if ($line =~ /smartctl|Copyright|Home page|===/); + + if ($line =~ /.*overall-health.*:\s*(.*)/) + { + my $lstatus = ($1 == 'PASSED') ? 'green' : 'red'; + print OUT "&$lstatus $line"; + } + elsif ($line =~ /^\s*(\d+)\s+(\S+)\s+\S+\s+(\d+)\s+(\d+)\s+(\d+)\s+\S+\s+\S+\s+\S+\s+(.*)$/) + { + my ($aname, $value, $worst, $thresh, $raw) = ($2, $3, $4, $5, $6); + my $lstatus = 'green'; + if ($aname =~ /Current_Pending_Sector|Offline_Uncorrectable/ and int($raw) > 0) + { + $lstatus = 'yellow'; + } + elsif ($aname =~ /Power_On_Hours/) + { + $ponhours = $raw; + } + + print OUT "&$lstatus $line"; + + $ncv .= "$name-$aname-value : $value\n"; + $ncv .= "$name-$aname-worst : $worst\n"; + $ncv .= "$name-$aname-thresh : $thresh\n"; + $ncv .= "$name-$aname-raw : $raw\n"; + } + elsif ($line =~ /^\s*No Errors Logged\s*$/) + { + $newerr = 0; + print OUT "&green $line"; + } + elsif ($line =~ /Error Count:\s*(\d+)/) + { + $newerr = $1 - $olderr{$name}; + my $lstatus = $newerr > 0 ? 'red' : 'green'; + print OUT "&$lstatus $line" + } + elsif ($line =~ /^\s*Error \d+ occurred/) + { + my $lstatus = $newerr > 0 ? 'red' : 'green'; + print OUT "&$lstatus $line" + } + elsif ($line =~ /^\s*#\s*\d+\s+(Conveyance offline|Extended offline|Short offline|Extended captive)\s+(.*)\s+\d+%\s+(\d+)/) + { + my $status = $2; + my $lifetime = $3; + my $lstatus = 'red'; + $lasttest = $lifetime if (!defined($lasttest)); + $lstatus = 'yellow' if ($status =~ /Aborted by host|Interrupted \(host reset\)/); + $lstatus = 'green' if ($status =~ /Completed without error|Self-test routine in progress|Interrupted \(host reset\)/); + print OUT "&$lstatus $line"; + } + else + { + print OUT " $line"; + } + } + # test status footer + my $lasttestage = $ponhours % 65536 - $lasttest; + my $lasttestmsg = "$lasttestage hours ago"; + my $lasttestcolor = 'green'; + if (!defined($lasttest)) + { + $lasttestcolor = 'yellow'; + $lasttestmsg = 'no test performed'; + } + elsif ($lasttestage > 24 * 7) + { + $lasttestcolor = 'red'; + } + elsif ($lasttestage > 24 * 2) + { + $lasttestcolor = 'yellow'; + } + print OUT "&$lasttestcolor Last Self-test: $lasttestmsg\n"; + + # hidden output for ncv + print OUT "\n"; + } + + close OUT; +} + +# send cached content +{ + open IN, $CACHEFILE or die "cannot open $CACHEFILE"; + while (my $line = ) + { + if ($line =~ /^\s*&(\S+)/) + { + $bb->color_print($1, $line); + } + else + { + $bb->print($line); + } + } + close IN; +} + + +$bb->send; + +sub load_config +{ + my $path = shift; + + open C, "<$path" or return; +# print "loading config from $path\n"; + while (my $line = ) + { + next if ($line =~ /^\s*#/); + if ($line =~ /DISKS\s*=\s*['"](.*?)["']/) + { + @disks = split(/\s+/, $1); + } + if ($line =~ /SMARTOLDERROR\[([\w\/]+)\]\s+(\d+)/) + { + $olderr{$1} = $2; + } + if ($line =~ /SMARTCACHETIME=(\d+)/) + { + $CACHETIME = $1; + } + } + close C; +}