Test major Koha Wiki changes or bug fixes here without fear of breaking the production wiki.
For the current Koha Wiki, visit https://wiki.koha-community.org .Scripts Library
This page includes scripts in different languages that can enhance, solve or automate some specific tasks. Note that code below has not been tested by koha developpers and should be read/test thoroughly before applying to your own system. Scripts in any code language can be added to this list.
Use them at your own risks!
Scripts Template
Script title
- Developer: Name of script developer
- Module: OPAC / STAFF / BOTH / Command line / NA
- Purpose: Purpose of the script
- Requirements: Any specific requirements or dependencies the script needs to run
- Coding Language: The coding language the script is written in.
- Instructions: Any specific instructions needed.
#!/usr/bin/perl
my code
...
...
Scripts
Send birthday greetings to patrons
- Developer: Alvaro Cornejo
- Module: NA - command line script
- Purpose: This script, when placed into a cronjob, will send a greetings email to patrons on their birthdate
- Requirements: a public report, set a cronjob
- Coding Language: perl
- Instructions: This script needs a public report that will pass patron info to script. Its report id shall be replaced on the script. The LIMIT 50 on the SQL can be anything. It is just there to override "hardcoded" records limit -10- on public reports.
#To run script manually:
/usr/bin/perl /PATHTOSCRIPT/koha_birthdays.pl
# To have crontab run the script everyday at 6am add:
0 6 * * * USER /usr/bin/perl /PATHTOSCRIPT/koha_birthdays.pl
The birthday script:
#!/usr/bin/perl
# Library includes
use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request;
use JSON;
#My Global Variables
my $hello = "";
my $subject = "";
my $body = "";
my $message = "";
my $to = "";
my $from = "";
my $result = "";
my $i = 0;
# Capture report data:
# Fields from report: surname, firstname, email, emailpro, sex, lang
# Prepare connection, call url and insert data (json format) to arrary
my $ua = new LWP::UserAgent;
$ua->agent("Perl API Client/1.0");
# Replace YOUROPACBASEDOMAIN with the base url of your OPAC
# Replace XX with your public report ID
my $url = "https://YOUROPACBASEDOMAIN/cgi-bin/koha/svc/report?id=XX";
my $request = HTTP::Request->new("GET" => $url);
my $response = $ua->request($request);
my $json_obj = JSON->new->utf8->decode($response->content);
my $row_num = scalar(@{$json_obj});
#Scroll/split each registry and send the email
foreach my $row (@{$json_obj}) {
if ($i < $row_num) {
my $surname = @{$row}[0];
my $firstname = @{$row}[1];
my $email = @{$row}[2];
my $emailpro = @{$row}[3];
my $sex = @{$row}[4];
my $lang = @{$row}[5];
# Get destination address (private else professional)
if ($email) {
$to = $email;
} elsif ($emailpro) {
$to = $emailpro;
} else {
next;
}
# Assemble the patron salutation string for each language. Our default is es-ES
if ($lang eq "default" || $lang eq "es-ES") {
if ($sex eq "M") {
$hello = "Estimado Sr. $surname,\n"
} elsif ($sex eq "F") {
$hello = "Estimada Sra. $surname,\n"
} else {
$hello = "Estimado(a) $firstname $surname,\n"
}
} elsif ($lang eq "en") {
if ($sex eq "M") {
$hello = "Dear Mr. $surname,\n"
} elsif ($sex eq "F") {
$hello = "Dear Mrs. $surname,\n"
} else {
$hello = "Dear $firstname $surname,\n"
}
}
# Define email subject & body. Language dependant
if ($lang eq "default" || $lang eq "es-ES") {
$subject = "Feliz cumpleaños le desea el CELACP!";
$body = "\n";
$body .= "El CELACP se complace en hacerle llegar sus mejores deseos en esta fecha tan importante para Ud.\n\n";
$body .= "Sinceramente esperamos tenga un muy feliz día y esperamos contar con su presencia en nuestro local\n";
$body .= "o a través de nuestra plataforma virtual en https://biblioteca.celacp.org\n\n";
$body .= "Atentamente,\n\n";
$body .= "Centro de Estudios Latinoaméricanos Antonio Cornejo Polar - CELACP -\n";
} elsif ($lang eq "en") {
$subject = "Happy birthday from the CELACP!";
$body = "\n";
$body .= "The CELACP is pleased to send you its best wishes on this important date for you.\n\n";
$body .= "We sincerely hope you have a very happy day and we look forward to seeing you at our library\n";
$body .= "or through our virtual platform at https://biblioteca.celacp.org\n\n";
$body .= "Sincerely,\n\n";
$body .= "Center for Latinamerican Estudies Antonio Cornejo Polar - CELACP -\n";
}
# Final message assembly
#$to = 'dummy@gmail.com'; # Dummy address for testing purposes. If uncommented, all emails will be sent to this address. Edit it for testing
$from = 'sourceemail@yourdomain.com'; # This might get overridden by your mail server
$message = $hello;
$message .= $body;
# Create mail "session"
open(MAIL, "|/usr/sbin/sendmail -t");
# Email Header
print MAIL "To: $to\n";
print MAIL "From: $from\n";
print MAIL "Subject: $subject\n\n";
# Email Body
print MAIL $message;
# Send email & confirm
$result = close(MAIL);
if($result) { print "A message has been sent to $to\n";} else { print "Message to $to failed!\n";}
$i++;
} # END IF
} # END FOREACH
print "$i brthday mensages has been processed\n";
1;
The associated birthday public report:
SELECT borrowers.surname, borrowers.firstname, borrowers.email, borrowers.emailpro, borrowers.sex, borrowers.lang
FROM borrowers
WHERE DATE_FORMAT(borrowers.dateofbirth, '%m-%d') = DATE_FORMAT(CURDATE(), '%m-%d')
LIMIT 50
Create a biblios by collection (records $ items) html table to be shown in news
- Developer: Alvaro Cornejo
- Module: NA - command line script
- Purpose: This script, when placed into a cronjob, will regularly update a html file containing a table of bilbio ordered by categories
- Requirements: a public report, set a cronjob
- Coding Language: perl
- Instructions: This script needs a public report that will povide biblio category counts info to script. Report id shall be replaced on the script. The LIMIT 50 on the SQL can be anything. It is just there to override "hardcoded" records limit -10- on public reports. html file generated shall be placed in opac/admin page root folder or other apache accesible folder.
# To run script manually:
/usr/bin/perl /PATHTOSCRIPT/SCRIPTFILE.pl
#To use crontab to run script every day at 7am add:
0 7 * * * USER /usr/bin/perl /PATHTOSCRIPT/SCRIPTFILE.pl
The script itself:
#!/usr/bin/perl
# Perl library includes
use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request;
use Number::Format 'format_number';
use JSON;
print "\nReport start\n";
my $ua = new LWP::UserAgent;
$ua->agent("Perl API Client/1.0");
# My global variables
my $match = 0;
my $type_opac = "";
my $type_admin = "";
my $collection = "";
my $table_body_opac = "";
my $table_body_admin = "";
my $table_head_admin = "";
my $table_foot_admin = "";
my $table_head_opac = "";
my $table_foot_opac = "";
my $table_head = "";
my $table_end = "";
my $table = "";
# Get report records and place them into an array:
# Fields from report: branchname, homebranch, biblionumber count, items count. Last line has total counts
my $url = "https://OPACDOMAIN/cgi-bin/koha/svc/report?id=XX"; ## UPDATE REPORT ID NUMBER
my $request = HTTP::Request->new("GET" => $url);
my $response = $ua->request($request);
my $json_obj = JSON->new->utf8->decode($response->content);
my $row_num = scalar(@{$json_obj});
# Build tables headers for admin and OPAC page
$table_head .= "<table class=center >\n";
$table_head_admin .= "<tr style='text-align:center'><th colspan=3><b> Registros por Colección </b></th></tr>\n";
$table_head_admin .= "<tr style='font-size: 10.0pt'><th><b> Colección </b></th><th><b> Registros </b></th><th><b> Items </b></th></tr>\n";
$table_head_opac .= "<tr style='text-align:center;color:#D68636'><th colspan=3><b> Registros por Colección </b></th></tr>\n";
$table_head_opac .= "<tr style='font-size: 10.0pt;color:#D68636'><th><b> Colección </b></th><th><b> Registros </b></th><th><b> Items </b></th></tr>\n";
my $i = 1;
# Parse array data and create HTML archive with table
foreach my $row (@{$json_obj}) {
if ($i < $row_num) {
#Fix "malformed" accented chars
my $check_a = chr(225) ;
my $check_e = chr(233) ;
my $check_i = chr(237) ;
my $check_o = chr(243) ;
my $check_u = chr(250) ;
my $check_n = chr(241) ;
@{$row}[0] =~ s/$check_a/á/ig;
@{$row}[0] =~ s/$check_e/é/ig;
@{$row}[0] =~ s/$check_i/í/ig;
@{$row}[0] =~ s/$check_o/ó/ig;
@{$row}[0] =~ s/$check_u/ú/ig;
@{$row}[0] =~ s/$check_n/ñ/ig;
if (@{$row}[1] eq "CELACP") {
$collection = @{$row}[1];
} else {
my @words = split / /, @{$row}[0];
$collection = $words[-3] . " " . $words[-2] . " " . $words[-1] . " ";
}
my $formattedr = format_number(@{$row}[2]);
my $formattedv = format_number(@{$row}[3]);
if ($i < $row_num ) {
# Build table body for admin page
$type_admin = '<a href="catalogue/search.pl?advsearch=1&limit=branch:' . @{$row}[1] . '"> '. $collection . "</a>";
$table_body_admin .= "<tr style='font-size: 9.0pt;color:#464646'><td style=text-align:left;> $type_admin </td>";
$table_body_admin .= "<td style=text-align:right;> $formattedr </td><td style=text-align:right;> $formattedv </td></tr>\n";
# Build table body for opac page
$type_opac = '<a href="opac-search.pl?advsearch=1&limit=branch:' . @{$row}[1] . '"> '. $collection . "</a>";
$table_body_opac .= "<tr style='font-size: 9.0pt;color:#464646'><td style=text-align:left;> $type_opac </td>";
$table_body_opac .= "<td style=text-align:right;> $formattedr </td><td style=text-align:right;> $formattedv </td></tr>\n";
} else {
# Build body admin
$type_admin = '<a href="catalogue/search.pl?advsearch=1&limit=branch:' . @{$row}[1] . '"> '. $collection . "</a>";
$table_body_admin .= "<tr style='font-size: 9.0pt;color:#464646'><td style=text-align:left;> $type_admin </td>";
$table_body_admin .= "<td style=text-align:right;> $formattedr </td><td style=text-align:right;> $formattedv </td></tr>\n";
# Build body OPAC
$type_opac = '<a href="/opac-search.pl?advsearch=1&limit=branch:' . @{$row}[1] . '"> '. $collection . "</a>";
$table_body_opac .= "<tr style='font-size: 9.0pt;color:#464646'><td style=text-align:left;> $type_opac </td>";
$table_body_opac .= "<td style=text-align:right;> $formattedr </td><td style=text-align:right;> $formattedv </td></tr>\n";
}
} else {
my $formattedr = format_number(@{$row}[2]);
my $formattedv = format_number(@{$row}[3]);
# Build tables footers for admin & opac pages
$table_foot_admin .= "<tr style='font-size: 10.0pt'><th style=text-align:center;><b> Total: </th>";
$table_foot_admin .= "<th style=text-align:right;><b> $formattedr </th><th style=text-align:right;><b> $formattedv </b></th></tr>\n";
$table_foot_opac .= "<tr style='font-size: 10.0pt;color:#D68636'><th style=text-align:center;><b> Total: </th>";
$table_foot_opac .= "<th style=text-align:right;><b> $formattedr </th><th style=text-align:right;><b> $formattedv </b></th></tr>\n";
}
$i++;
}
$table_end .= "</table><br>\n";
# Assemble tables for OPAC and admin
my $table_opac = $table_head . $table_head_opac . $table_body_opac . $table_foot_opac . $table_end;
my $table_admin = $table_head . $table_head_admin . $table_body_admin . $table_foot_admin . $table_end;
# Write intranet table file:
my $filename_admin = "/usr/share/koha/intranet/htdocs/report.html";
open(FH, '>', $filename_admin) or warn "Couldn´t write $filename_admin: $!";
print FH $table_admin;
close(FH);
print "File $filename_admin written \n";
# Write OPAC table file:
my $filename_opac = "/usr/share/koha/opac/htdocs/report.html";
open(FH, '>', $filename_opac) or warn "Couldn´t write file $filename_opac: $!";
print FH $table_opac;
close(FH);
print "File $filename_opac written \n";
1;
Associated SQL query:
SELECT branchname AS Colección, homebranch AS Code, count( DISTINCT biblionumber) AS Registos, count(itemnumber) AS Items
FROM items
LEFT JOIN branches ON (items.homebranch=branches.branchcode)
GROUP BY branchname
WITH ROLLUP LIMIT 50
In order to have html file appear in news, add this code to any news entry. Path shall be relative to opac/admin base url
...
<span >Catalog summary by Collection</span>
<span w3-include-html="../../../report.html"></span>
...
The resulting table:
Koha services/server monitor
- Developer: Alvaro Cornejo
- Module: NA - command line script - Koha/server monitor table. Resulting table can be shown anywhere in admin/opac
- Purpose: This script, when placed into a cronjob, will monitor koha services and get some basic server components stats.
- Requirements: Set a cronjob, script from nixCraft to monitor server ports (included below), CLI linux commands (uptime, df, memcstat, ps, top, free)
- Coding Language: perl
- Instructions: This script run several linux commands to get server data, listening ports and monitor koha specific processes and generates a html file. This file (table) can be placed in admin page news and using ajax can be refreshed as desired. The script can be added to cron job to be updated as required.
#To run script manually:
/usr/bin/perl /PATHTOSCRIPT/SCRIPTFILE.pl
#To use crontab, with a 30 second refresh interval, add:
*/1 * * * * USER /usr/bin/perl /PATHTOSCRIPT/SCRIPTFILE.pl
*/1 * * * * USER sleep 30; /usr/bin/perl /PATHTOSCRIPT/SCRIPTFILE.pl
The resulting table:
Processes counts:
Ap: apache, Ze: zebra, Pl: plack, Me: memcached, Ko: koha, My: mysql, Em: postfix
The script itself
#!/usr/bin/perl
# My includes
use strict;
use warnings;
use Number::Format 'format_number','format_bytes';
use POSIX qw(strftime);
### MY monitorig commands
my $cmd_srv_uptime = "uptime | awk '{print \$3, \$4, \$5}'";
my $cmd_svc_mon = "/PATHTOSCRIPT/runsvcmon.sh"; #ADJUST PATH AS REQUIRED
my $cmd_memca_stats="memcstat --servers=localhost 11211";
my $cmd_apache = "ps -ef |grep apache |wc -l"; # "normal" > 18
my $cmd_memca = "ps -ef |grep memcached |wc -l"; # "normal" = 1
my $cmd_mysql = "ps -ef |grep mysql |wc -l"; # "normal" = 1
my $cmd_mail = "ps -ax |grep postfix |wc -l"; # "normal" = 1
my $cmd_zebra = "ps -ef |grep zebra |wc -l"; # "normal" = 5
my $cmd_plack = "ps -ef |grep plack |wc -l"; # "normal" = 3
my $cmd_koha = "ps -ef |grep koha |wc -l"; # "normal" = 9
my $cmd_disk_use = "df |grep sda | awk '{print \$3, \$5}'"; # 3->bytes 5->%
my $cmd_cpu_use = "top -n 2 -b |grep Cpu |awk '{print \$3}'"; # read 2nd lecture. 1st includes top load
my $cmd_cpu_avg = "top -n 1 -b |grep -i load |awk '{print \$11, \$12, \$13, \$14, \$15, \$16}'"; # gets 3[||||| ]... split in "[" to extract value
my $cmd_swap_use = "free -b |grep Swap | awk '{print \$2, \$3}'"; # 2->total 3->used
my $cmd_mem_use = "free -b |grep Mem | awk '{print \$2, \$7}'"; # 2->total 7->available
### OTHER MONITOR COMMANDS THAT MIGHT BE IMPLEMENTED
# my cmd_chk_mysql = "mysqladmin --user=AMYSQLUSER --password=MYPASSWORD status";
# my cmd_chk_opac = "curl -I "http://biblioteca.celacp.org" 2>&1 | awk '/HTTP\// {print $2}'" # shall return 301(Redirected)
# my cmd_chk_admin = "curl -I "http://biblioteca.celacp.org:8080" 2>&1 | awk '/HTTP\// {print $2}'" # shall return 200(OK)
# my cmd_chk_ssh = "ssh -q user@server.com exit | echo $?"; # 0 = OK, else NOK
# curl -I "https://biblioteca.celacp.org" 2>&1 | grep -w "200\|301"
# ping -c 10 www.google.com |grep received |awk '{print $6}' # % lost pings default 1 ping/second -i 0.5 two per second
# wc -l /var/log/koka/biblioteca/opac_error -> check logs number of lines per hour and warn if there is an "unusual" number of extra lines.
# Option: check log size every hour
#My Global Variables - process counters
my ($apache_wc, $memca_wc, $mysql_wc, $plack_wc, $koha_wc, $zebra_wc, $disk_use, $cpu_use, $mem_use, $swap_use, $mail_wc );
# Fonts color variables
my ($apache_wc_fntc, $memca_wc_fntc, $mysql_wc_fntc, $plack_wc_fntc, $koha_wc_fntc, $zebra_wc_fntc, $disk_use_fntc, $cpu_use_fntc, $mem_use_fntc, $swap_use_fntc, $mail_wc_fntc );
#My Global Variables - arrays
my (@t, @out, @out_svc, @parts);
# My global variables - Services
my ($ssh_stat_alt, $ssh_stat_img, $dns_stat_alt, $dns_stat_img, $web_stat_alt, $web_stat_img, $mail_stat_alt, $mail_stat_img);
my ($kohaadmin_stat_alt, $kohaadmin_stat_img, $kohaopac_stat_alt, $kohaopac_stat_img, $memca_stat_alt, $memca_stat_img, $mysql_stat_alt, $mysql_stat_img);
my ($stat1, $stat2, $stat3, $stat4, $stat5, $stat6, $stat7, $stat8, $stat9, $stat10, $stat11, $stat12, $stat13, $stat14);
my ($get_hits, $get_misses, $memc_evict);
my ($used_mem_icon, $used_mem_icon_alt, $used_swap_icon, $used_swap_icon_alt, $used_disk_icon, $used_disk_icon_alt, $get_hits_icon, $get_hits_icon_alt);
my ($used_cpu_icon, $used_cpu_icon_alt, $proc_num_icon, $proc_num_icon_alt, $mail_icon, $mail_icon_alt);
my ($used_swap_percent, $used_mem_percent, $used_disk, $used_cpu_avg, $used_cpu, $cpu_avg, $used_mem, $get_hits_percent);
#My Global Variables - Others
my ($exit, $i, $t, $s);
my ($table_body_proc, $table_head_proc, $table_foot_proc, $table_body_use, $table_head_use, $table_foot_use, $table_head, $table_end);
my ($mail_stat_fntc, $kohaadmin_stat_fntc, $ssh_stat_fntc, $dns_stat_fntc, $web_stat_fntc, $kohaopac_stat_fntc, $mysql_stat_fntc, $memca_stat_fntc);
# Define images for statuses:
my $ok_img = "/images/ok_23x20_tr.png";
my $nok_img = "/images/nok_23x20_tr.png";
my $check_img = "/images/check_23x20_tr.png";
my $cross_img = "/images/cross_23x20_tr.png";
my $warn_img = "/images/w_brush_23x20_tr.png";
my $red_warn_img = "/images/w_red_23x20_tr.png";
# Initialize some variables
$ssh_stat_img = $dns_stat_img = $mail_stat_img = $web_stat_img = $nok_img;
$kohaadmin_stat_img = $kohaopac_stat_img = $memca_stat_img = $mysql_stat_img = $nok_img;
$ssh_stat_alt = $dns_stat_alt = $mail_stat_alt = $web_stat_alt = "Servicio caído";
$kohaadmin_stat_alt = $kohaopac_stat_alt = $memca_stat_alt = $mysql_stat_alt = "Servicio caído";
$apache_wc_fntc = $memca_wc_fntc = $mail_wc_fntc= $mysql_wc_fntc = $plack_wc_fntc = $koha_wc_fntc = $zebra_wc_fntc = "";
# Some "cleaning" functions
sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s }; # remove trailing spaces
sub trimLF { my $s = shift; $s =~ s/\n//g; return $s }; # remove new lines
sub trimColon { my $s = shift; $s =~ s/,//g;; return $s }; # remove colon
sub trimPercent { my $s = shift; $s =~ s/%//g;; return $s }; # remove % sign
####################################################################
### Number of running process
# Execute each commnad to get the number of running processes:
# Remove two process from count. Since one is the grep command itself and the other one that perl generates when executing command.
$apache_wc = `$cmd_apache` -2;
$zebra_wc = `$cmd_zebra` -2;
$plack_wc = `$cmd_plack` -2;
$memca_wc = `$cmd_memca` -2;
$mail_wc = `$cmd_mail` -2;
$koha_wc = `$cmd_koha` -2;
$mysql_wc = `$cmd_mysql` -2;
# If any of the process is not running, error image and red font to the whole process line
if ($apache_wc == 0 || $zebra_wc == 0 || $plack_wc == 0 || $memca_wc == 0 || $koha_wc == 0 || $mysql_wc == 0 || $mail_wc == 0) {
$proc_num_icon = $nok_img;
$proc_num_icon_alt = "A monitores process is down";
$apache_wc_fntc = $memca_wc_fntc = $mysql_wc_fntc = $plack_wc_fntc = $koha_wc_fntc = $zebra_wc_fntc = $disk_use_fntc = $mem_use_fntc = $swap_use_fntc = $mail_wc_fntc = " color=red ";
} else {
# For each process we define "normal" count of running processes. "count" shall be adjusted as "normal usage" counts. If count out of range then warning (icon and orange font)
$proc_num_icon = $ok_img;
$proc_num_icon_alt = "Procesos ok";
if ($apache_wc > 25 || $apache_wc < 5 ) {
$apache_wc_fntc = " color=orange ";
$proc_num_icon = $warn_img;
$proc_num_icon_alt = "Apache warn";
}
if ($zebra_wc > 8 || $zebra_wc < 5 ) {
$zebra_wc_fntc = " color=orange ";
$proc_num_icon = $warn_img;
$proc_num_icon_alt = "Zebra warn";
}
if ($plack_wc > 3 || $plack_wc < 2) {
$plack_wc_fntc = " color=orange ";
$proc_num_icon = $warn_img;
$proc_num_icon_alt = "Plack warn";
}
if ($memca_wc != 1 ) {
$memca_wc_fntc = " color=orange ";
$proc_num_icon = $warn_img;
$proc_num_icon_alt = "Memca warn";
}
if ($mail_wc != 1 ) {
$mail_wc_fntc = " color=orange ";
$proc_num_icon = $warn_img;
$mail_icon_alt = "Mail warn";
}
if ($mysql_wc != 1 ) {
$mysql_wc_fntc = " color=orange ";
$proc_num_icon = $warn_img;
$proc_num_icon_alt = "Proc num warn";
}
if ($koha_wc > 15 || $koha_wc < 5 ) {
$koha_wc_fntc = " color=orange ";
$proc_num_icon = $warn_img;
$proc_num_icon_alt = "Koha warn";
}
}
# assemble processes line to place in table.
my $proc_list = "<font $apache_wc_fntc>Ap:$apache_wc</font> <font $zebra_wc_fntc>Ze:$zebra_wc</font> <font $plack_wc_fntc>Pl:$plack_wc</font> <font $memca_wc_fntc>Me:$memca_wc</font> <font $koha_wc_fntc>Ko:$koha_wc</font> <font $mysql_wc_fntc>My:$mysql_wc</font> <font $mail_wc_fntc>Em:$mail_wc</font>";
print "proc_list: $proc_list\n"; # Just to check from the command line. Can be removed/commented out
######################################################################################################
#### /SDA DISK USAGE GB & %
$disk_use = `$cmd_disk_use`;
$exit = `echo $?`;
if ( $exit != 0 ) { # command fails for any reason
print "Falló disk usage\n";
$used_disk = "No disk info!";
$used_disk_icon = $red_warn_img;
$used_disk_icon_alt = "No disk info!";
$disk_use_fntc = " id=\"info_red\" ";
} else {
@t=split(" ",$disk_use);
my $used_disk_kb = $t[0] * 1024;
my $used_disk_percent = trimPercent($t[1]);
if ($used_disk_percent <= 70) {
$used_disk_icon = $ok_img;
$used_disk_icon_alt = "used disk ok";
$disk_use_fntc = " id=\"info\" ";
} else {
if ($used_disk_percent > 70 && $used_disk_percent <= 90) {
$used_disk_icon = $warn_img;
$used_disk_icon_alt = "used disk warn";
$disk_use_fntc = " id=\"info_orange\" ";
} else {
if ($used_disk_percent > 90) {
$used_disk_icon = $red_warn_img;
$used_disk_icon_alt = "used disk high";
$disk_use_fntc = " id=\"info_red\" ";
}
}
}
$used_disk_kb = format_bytes($used_disk_kb);
$used_disk = "$used_disk_kb ($used_disk_percent%)";
}
print "used_disk: $used_disk\n";
######################################################################################################
#### CPU USAGE TOTAL AND AVG
# GET commands results and format values for table
$cpu_avg = `$cmd_cpu_avg`;
$exit = `echo $?`;
if ( $exit != 0 ) {
print "Falló CPU avg \n";
$used_cpu_avg = "No CPU info!"
} else {
@t=split(" ",$cpu_avg);
my $rows = scalar(@t);
$used_cpu_avg = trimColon($t[$rows-3]) . "% " . trimColon($t[$rows-2])."% " . trimColon(trimLF($t[$rows-1]))."%";
}
$cpu_use = `$cmd_cpu_use`;
$exit = `echo $?`;
if ( $exit != 0 ) {
print "Falló CPU usage \n";
$used_cpu = "No CPU info!";
$used_cpu_icon = $red_warn_img;
$used_cpu_icon_alt = "No CPU Info!";
$cpu_use_fntc = " id=\"info_red\" ";
} else {
@t=split("\n",$cpu_use);
@t=split('\[',$t[1]);
my $used_cpu_percent = $t[0];
if ($used_cpu_percent <= 50) {
$used_cpu_icon = $ok_img;
$used_cpu_icon_alt = "used cpu ok";
$cpu_use_fntc = " id=\"info\" ";
} else {
if ($used_cpu_percent > 50 && $used_cpu_percent <= 80) {
$used_cpu_icon = $warn_img;
$used_cpu_icon_alt = "used cpu warn";
$cpu_use_fntc = " id=\"info_orange\" ";
} else {
if ($used_cpu_percent > 80) {
$used_cpu_icon = $red_warn_img;
$used_cpu_icon_alt = "used cpu high";
$cpu_use_fntc = " id=\"info_red\" ";
}
}
}
$used_cpu = "$used_cpu_percent% ($used_cpu_avg)";
}
print "used_cpu: $used_cpu\n";
######################################################################################################
#### MEM USAGE y %
$mem_use = `$cmd_mem_use`;
$exit = `echo $?`;
if ( $exit != 0 ) {
print "Falló MEM usage \n";
$used_mem = "No mem info";
$used_mem_icon = $red_warn_img;
$used_mem_icon_alt = "No mem info";
$mem_use_fntc = " id=\"info_red\" ";
} else {
@t=split(" ",$mem_use);
if ($t[0] == 0) { $used_mem_percent = 0;
} else { $used_mem_percent = 100-(($t[0]-$t[1])/$t[0]*100); }
$used_mem_percent = format_number($used_mem_percent);
if ($used_mem_percent <= 75) {
$used_mem_icon = $ok_img;
$used_mem_icon_alt = "used mem ok";
$mem_use_fntc = " id=\"info\" ";
} else {
if ($used_mem_percent > 75 && $used_mem_percent <= 90) {
$used_mem_icon = $warn_img;
$used_mem_icon_alt = "used mem warn";
$mem_use_fntc = " id=\"info_orange\" ";
} else {
if ($used_mem_percent > 90) {
$used_mem_icon = $red_warn_img;
$used_mem_icon_alt = "used mem high";
$mem_use_fntc = " id=\"info_red\" ";
}
}
}
$used_mem = $t[0]-$t[1];
$used_mem = format_bytes($used_mem) . " ($used_mem_percent%)";
}
print "used_mem: $used_mem\n";
######################################################################################################
#### SWAP USAGE y %
$swap_use = `$cmd_swap_use`;
$exit = `echo $?`;
if ( $exit != 0 ) {
print "Falló SWAP usage \n";
$used_swap_icon = $red_warn_img;
$used_swap_icon_alt = "No swap info";
$swap_use_fntc = " id=\"info_red\" ";
}
@t=split(" ",$swap_use);
if ($t[0] == 0) { $used_swap_percent = 0;
} else { $used_swap_percent = $t[1]/$t[0]*100; }
$used_swap_percent = format_number($used_swap_percent);
if ($used_swap_percent <= 70) {
$used_swap_icon = $ok_img;
$used_swap_icon_alt = "used swap ok";
$swap_use_fntc = " id=\"info\" ";
} else {
if ($used_swap_percent > 70 && $used_swap_percent <= 90) {
$used_swap_icon = $warn_img;
$used_swap_icon_alt = "used swap warn";
$swap_use_fntc = " id=\"info_orange\" ";
} else {
if ($used_swap_percent > 90) {
$used_swap_icon = $red_warn_img;
$used_swap_icon_alt = "used swap high";
$swap_use_fntc = " id=\"info_red\" ";
}
}
}
my $used_swap = $t[1];
$used_swap = format_bytes($used_swap) . " ($used_swap_percent%)";
#print "used_swap_percent: $used_swap_percent\n";
print "used_swap: $used_swap\n";
print "------------------------\n";
########################################################################################################
# Capturo stats de memcached
@out = `$cmd_memca_stats`;
$exit = `echo $?`;
if ( $exit != 0 )
{
print "Falló info de memcached\n";
$get_hits_icon = $nok_img;
$get_hits_icon_alt = "No memc info";
$get_hits = "No memc info";
} else {
for ($i=0; $i<@out; $i++)
{
if ($out[$i] =~ /evictions:/) {
@t=split(" ",$out[$i]);
$memc_evict = $t[1];
print "memc_evict: $memc_evict\n";
}
if ($out[$i] =~ /get_hits/) {
@t=split(" ",$out[$i]);
$get_hits = $t[1];
}
if ($out[$i] =~ /get_misses/) {
@t=split(" ",$out[$i]);
$get_misses = $t[1];
}
} # END FOR
if ($get_hits == 0 || $get_misses == 0) {
$get_hits_percent = 0;
} else {
$get_hits_percent = format_number($get_hits / ($get_hits + $get_misses) * 100);
if ($get_hits_percent > 95) {
$get_hits_icon = $ok_img;
$get_hits_icon_alt = "memc gets ok";
} else {
if ($get_hits_percent > 70 && $get_hits_percent <= 95) {
$get_hits_icon = $warn_img;
$get_hits_icon_alt = "memc gets warn";
} else {
if ($get_hits_percent <= 70) {
$get_hits_icon = $nok_img;
$get_hits_icon_alt = "memc gets error";
}
}
}
$get_hits = format_bytes($get_hits, base => 1000) . "(" . $get_hits_percent . "%)";
}
}
print "get_hits: $get_hits\n";
######################################################################################################
# Capturo status de servicios
@out_svc = `$cmd_svc_mon`;
$exit = `echo $?`;
if ( $exit != 0 )
{
print "Falló info de memcached\n";
$get_hits_icon = $nok_img;
$get_hits_icon_alt = "No memc info";
$get_hits = "No memc info";
} else {
my $svc_num = scalar(@out_svc);
for ($i=0; $i<@out_svc; $i++) {
if ($out_svc[$i] =~ /SSH/) {
@t=split(" ",$out_svc[$i]);
if ($t[1] =~ /YES/) {
$ssh_stat_img = $ok_img;
$ssh_stat_alt = "SSH OK";
$ssh_stat_fntc = " id=\"svc\" ";
} else {
$ssh_stat_img = $cross_img;
$ssh_stat_alt = "SSH ERROR";
$ssh_stat_fntc = " id=\"svc_red\" ";
}
}
if ($out_svc[$i] =~ /DNS/) {
@t=split(" ",$out_svc[$i]);
if ($t[1] =~ /YES/) {
$dns_stat_img = $ok_img;
$dns_stat_alt = "DNS OK";
$dns_stat_fntc = " id=\"svc\" ";
} else {
$dns_stat_img = $cross_img;
$dns_stat_alt = "DNS ERROR";
$dns_stat_fntc = " id=\"svc_red\" ";
}
}
if ($out_svc[$i] =~ /WEB/) {
@t=split(" ",$out_svc[$i]);
if ($t[1] =~ /YES/) {
$web_stat_img = $ok_img;
$web_stat_alt = "WEB OK";
$web_stat_fntc = " id=\"svc\" ";
} else {
$web_stat_img = $cross_img;
$web_stat_alt = "WEB ERROR";
$web_stat_fntc = " id=\"svc_red\" ";
}
}
if ($out_svc[$i] =~ /MAIL/) {
@t=split(" ",$out_svc[$i]);
if ($t[1] =~ /YES/) {
$mail_stat_img = $ok_img;
$mail_stat_alt = "MAIL OK";
$mail_stat_fntc = " id=\"svc\" ";
} else {
$mail_stat_img = $cross_img;
$mail_stat_alt = "MAIL ERROR";
$mail_stat_fntc = " id=\"svc_red\" ";
}
}
if ($out_svc[$i] =~ /KOHA-ADMIN/) {
@t=split(" ",$out_svc[$i]);
if ($t[1] =~ /YES/) {
$kohaadmin_stat_img = $ok_img;
$kohaadmin_stat_alt = "ADMIN OK";
$kohaadmin_stat_fntc = " id=\"svc\" ";
} else {
$kohaadmin_stat_img = $cross_img;
$kohaadmin_stat_alt = "ADMIN ERROR";
$kohaadmin_stat_fntc = " id=\"svc_red\" ";
}
}
if ($out_svc[$i] =~ /KOHA-OPAC/) {
@t=split(" ",$out_svc[$i]);
if ($t[1] =~ /YES/) {
$kohaopac_stat_img = $ok_img;
$kohaopac_stat_alt = "OPAC OK";
$kohaopac_stat_fntc = " id=\"svc\" ";
} else {
$kohaopac_stat_img = $cross_img;
$kohaopac_stat_alt = "OPAC ERROR";
$kohaopac_stat_fntc = " id=\"svc_red\" ";
}
}
if ($out_svc[$i] =~ /MEMCACHED/) {
@t=split(" ",$out_svc[$i]);
if ($t[1] =~ /YES/) {
$memca_stat_img = $ok_img;
$memca_stat_alt = "MEMCACHED OK";
$memca_stat_fntc = " id=\"svc\" ";
} else {
$memca_stat_img = $cross_img;
$memca_stat_alt = "MEMECACHE ERROR";
$memca_stat_fntc = " id=\"svc_red\" ";
}
}
if ($out_svc[$i] =~ /MYSQL/) {
@t=split(" ",$out_svc[$i]);
if ($t[1] =~ /YES/) {
$mysql_stat_img = $ok_img;
$mysql_stat_alt = "MYSQL OK";
$mysql_stat_fntc = " id=\"svc\" ";
} else {
$mysql_stat_img = $cross_img;
$mysql_stat_alt = "MYSQL ERROR";
$mysql_stat_fntc = " id=\"svc_red\" ";
}
}
} # END for @out_svc
} # END else exit
######################################################################################################
### NOW assembly the monitor table
my $table_style = "<style>
td {
height:20px;
text-align:center;
vertical-align:middle;
font-size:9.0pt;
}
#info {
height:20px;
text-align:left;
vertical-align:middle;
font-size:9.0pt;
}
#info_red {
color:red;
height:20px;
text-align:left;
vertical-align:middle;
font-size:9.0pt;
}
#info_orange {
color:orange;
height:20px;
text-align:left;
vertical-align:middle;
font-size:9.0pt;
}
#svc {
height:20px;
text-align:center;
vertical-align:middle;
font-size:9.0pt;
}
#svc_red {
color:red;
height:20px;
text-align:center;
vertical-align:middle;
font-size:9.0pt;
}
</style>";
$table_head = "";
my $table_svc_stat_head = "<table>\n";
$table_svc_stat_head .= "<tr><th><b> Servicio </b></th><th><b> Status </b></th>";
$table_svc_stat_head .= "<th><b> Servicio </b></th><th><b> Status </b></th></tr>\n";
my $table_svc_stat_body .= "<tr><td $ssh_stat_fntc > SSH </td>";
$table_svc_stat_body .= "<td><img alt=\"$ssh_stat_alt\" src=\"$ssh_stat_img\" </td>";
$table_svc_stat_body .= "<td $dns_stat_fntc > DNS </td>";
$table_svc_stat_body .= "<td><img id=\"ssh\" alt=\"$dns_stat_alt\" src=\"$dns_stat_img\" </td></tr>\n";
$table_svc_stat_body .= "<tr><td $mail_stat_fntc > MAIL </td>";
$table_svc_stat_body .= "<td><img id=\"mail\"alt=\"$mail_stat_alt\" src=\"$mail_stat_img\" </td>";
$table_svc_stat_body .= "<td $web_stat_fntc > WEB </td>";
$table_svc_stat_body .= "<td><img alt=\"$web_stat_alt\" src=\"$web_stat_img\" </td></tr>\n";
$table_svc_stat_body .= "<tr><td $kohaadmin_stat_fntc > ADMIN </td>";
$table_svc_stat_body .= "<td><img alt=\"$kohaadmin_stat_alt\" src=\"$kohaadmin_stat_img\" </td>";
$table_svc_stat_body .= "<td $kohaopac_stat_fntc > OPAC </td>";
$table_svc_stat_body .= "<td><img alt=\"$kohaopac_stat_alt\" src=\"$kohaopac_stat_img\" </td></tr>\n";
$table_svc_stat_body .= "<tr><td $memca_stat_fntc > MEMCA </td>";
$table_svc_stat_body .= "<td><img alt=\"$memca_stat_alt\" src=\"$memca_stat_img\" </td>";
$table_svc_stat_body .= "<td $mysql_stat_fntc > MYSQL </td>";
$table_svc_stat_body .= "<td><img alt=\"$mysql_stat_alt\" src=\"$mysql_stat_img\" </td></tr>\n";
my $table_other_info = "<tr><th colspan=4><b> Información del servidor </b></th></tr>";
$table_other_info .= "<tr><td $disk_use_fntc colspan=3 >Disk usage:\t$used_disk</td>";
$table_other_info .= "<td><img alt=\"$used_disk_icon_alt\" src=\"$used_disk_icon\" </td></tr>\n";
$table_other_info .= "<tr><td $cpu_use_fntc colspan=3 > CPU usage:<br>$used_cpu</td>";
$table_other_info .= "<td><img alt=\"$used_cpu_icon_alt\" src=\"$used_cpu_icon\" </td></tr>\n";
$table_other_info .= "<tr><td $mem_use_fntc colspan=3 >Mem usage:\t$used_mem</td>";
$table_other_info .= "<td><img alt=\"$used_mem_icon_alt\" src=\"$used_mem_icon\" </td></tr>\n";
$table_other_info .= "<tr><td $swap_use_fntc colspan=3 >Swap usage:\t$used_swap</td>";
$table_other_info .= "<td><img alt=\"$used_swap_icon_alt\" src=\"$used_swap_icon\" </td></tr>\n";
$table_other_info .= "<tr><td id=\"info\" colspan=3 > Memc monit:\tEv:$memc_evict Hits:$get_hits</td>";
$table_other_info .= "<td><img alt=\"$get_hits_icon_alt\" src=\"$get_hits_icon\" </td></tr>\n";
$table_other_info .= "<tr><td id=\"info\" colspan=3 > Monitor de procesos:<br>$proc_list </td>";
$table_other_info .= "<td><img alt=\"$proc_num_icon_alt\" src=\"$proc_num_icon\" </td></tr>\n";
$table_end .= "</table>\n";
# -- ASSEMBLE RESULT TABLES
my $table_svc = $table_style . $table_head . $table_svc_stat_head . $table_svc_stat_body . $table_other_info . $table_end;
######################################################################################################
### Write the whole to disk
# Write html table to the admin base url
my $filename_admin = "/usr/share/koha/intranet/htdocs/svc_mon.html";
open(FH, '>', $filename_admin) or warn "No se puedo guardar el archivo $filename_admin: $!";
print FH $table_svc;
close(FH);
######################################################################################################
1;
The nixCraft shell script (runsvcmon.sh) to get listening ports:
#!/bin/bash
# Shell script to monitor running services such as web/http, ssh, mail etc.
# If service fails it will send an Email to ADMIN user
# -------------------------------------------------------------------------
# Copyright (c) 2006 nixCraft project <http://www.cyberciti.biz/fb/>
# This script is licensed under GNU GPL version 2.0 or above
# -------------------------------------------------------------------------
# This script is part of nixCraft shell script collection (NSSC)
# Visit http://bash.cyberciti.biz/ for more information.
# ----------------------------------------------------------------------
# Last updated: Jun - 15 - 2009.
# Sendmail, root user and syslog functions disabled. Uncomment to enable
# Can ADD any additional port you want to check if its listening
ports="22 53 80 25 8080 443 11211 3306"
# service names as per above ports
# Assign to each port a name. In the same order as above.
service="SSH DNS WEB MAIL KOHA-ADMIN KOHA-OPAC MEMCACHED MYSQL"
#Email id to send alert
#ADMINEMAIL="admin@myispname.com"
#Bin paths, set them according to your Linux distro
NETSTAT=/bin/netstat
MAIL=/usr/bin/mail
LOGGER=/usr/bin/logger
ID=/usr/bin/id
# Red hat usr uncomment
#MAIL=/bin/mail
#LOGGER=/bin/logger
#Counters, set defaults
count=1
status="NO"
#sendmail=0
# set the following to 1, if you want message in /var/log/messages via a SYSLOG
logtosyslog=0
# Log file used to send an email
LOG="/tmp/services.log.$$"
# log message to screen and a log file
log(){
echo "$@"
echo "$@" >> $LOG
}
# log message and stop script
die(){
echo "$@"
exit 999
}
# Make sure only root can run it
#is_root(){
# local id=$($ID -u)
# [ $id -ne 0 ] && die "You must be root to run $0."
#}
# Look out for all bins and create a log file
init_script(){
[ ! -x $MAIL ] && die "$MAIL command not found."
[ ! -x $NETSTAT ] && die "$NETSTAT command not found."
[ ! -x $LOGGER ] && die "$LOGGER command not found."
[ ! -x $ID ] && die "$ID command not found."
# is_root
>$LOG
}
# check for all running services and shoot an email if service is not running
chk_services(){
# get open ports
REPORTS=$($NETSTAT -tulpn | grep -vE '^Active|Proto' | grep 'LISTEN' | awk '{ print $4}' | awk -F: '{print $NF}' | sed '/^$/d' | sort -u)
# okay let us compare them
for t in $ports
do
# ststus="NO"
sname=$(echo $service | cut -d' ' -f$count)
echo -en " $sname: "
echo -en " $sname: " >> $LOG
for r in $REPORTS
do
if [ "$r" == "$t" ]
then
status="YES"
#sendmail=1
break
fi
done
#if [ “$status” == “NO” ]
#then
# sendmail=1
#fi
echo -n "$status"
echo ""
echo -n "$status" >>$LOG
echo "" >>$LOG
# Log to a syslog /var/log/messages?
# This is useful if you have a dedicated syslog server
[ $logtosyslog -eq 1 ] && $LOGGER "$sname service running : $status"
# Update counters for next round
count=$( expr $count + 1 )
status="NO"
done
#if [ $sendmail -eq 1 ];
#then
# $MAIL -s "Service Down @ $(hostname)" $ADMINEMAIL < $LOG
#fi
}
### main ###
init_script
chk_services
### remove a log file ###
[ -f $LOG ] && /bin/rm -f $LOG
Create an admin news entry and add the following code to have it show in admin news and being refresh as desired
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"> </script>
<div id="koha_mon"><span w3-include-html="../../../svc_mon.html"></span></div>
<script type="text/javascript">
$(document).ready(function () {
setInterval(function(){ $('#koha_mon').load('/svc_mon.html');}, 5000) /* refresh time in milliseconds*/
});
</script>