Linux: How to receive warning email from a server when not much hard drive space left? - linux

I am building a new CentOS 6.4 server.
I was wondering if there is a way I can receive a warning email when the use of any partition exceeds 80% in the server.
EDIT:
As Aaron Digulla pointed out, this question is better suited for Server Fault.
Please view or answer this question in the following post in Server Fault.
https://serverfault.com/questions/570647/linux-how-to-receive-warning-email-from-a-server-when-not-much-hard-drive-space
EDIT:
Server Fault put my post on hold. I guess I have no choice but continue this post here.
As Sayajin suggested, the following script can do the trick.
usage=$(df | awk '{print $1,$5}' | tail -n +2 | tr -d '%');
echo "$usage" | while read FS PERCENT; do [ "$PERCENT" -ge "80" ] && echo "$FS has used ${PERCENT}% Disk Space"; done
This is exactly what I want to do. However for my case, the df output looks something like this:
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/VolGroup-LogVol01
197836036 5765212 182021288 4% /
As you see, filesystem and Use% are not in the same line. This causes $1 and $5 are not the info I want to get. Any idea to fix this?
Thanks.
EDIT:
The trick is
df -P
I also found shell script example in the following link doing exactly the same thing:
http://bash.cyberciti.biz/monitoring/shell-script-monitor-unix-linux-diskspace/

Install a monitoring service like Nagios.

You could always create a bash script & then have it email you:
usage=$(df | awk '{print $1,$5}' | tail -n +2 | tr -d '%');
echo "$usage" | while read FS PERCENT; do [ "$PERCENT" -ge "80" ] && echo "$FS has used ${PERCENT}% Disk Space"; done
Obviously instead of the && echo "$FS has used ${PERCENT}% Disk Space" you would send the warning email.

For people who do not have a monitoring system like Nagios (as suggested by #Aaron Digulla), this simple script can do the job :
#!/bin/bash
CURRENT=$(df / | grep / | awk '{ print $5}' | sed 's/%//g')
THRESHOLD=90
if [ "$CURRENT" -gt "$THRESHOLD" ] ; then
mail -s 'Disk Space Alert' mailid#domainname.com << EOF
Your root partition remaining free space is critically low. Used: $CURRENT%
EOF
fi
Then just add a cron job.

Related

Linux finding the mean of the memory usage of last hour

i am trying to write a script that finds the mean of the memory usage of last hour and if it's above %60, mails to someone thats relevant.
I am trying this for days and i am completely lost. On the other hand i can't get any updates for my Ubuntu so i can't try someting like atop. I need this to work on other computers as well.
As far as i know ;
free -m | awk 'NR==2{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }'
I am trying to use something like this in my code. Any help would appreciated.
Thanks.
EDIT
So i've built my scripts basics. But in this script i am getting the current ram usage.
#!/bin/sh
used=$(free -m | grep '^Mem' | awk '{print $3}')
total=$(free -m | grep '^Mem' | awk '{print $2}')
perct=$((($used*100)/$total))
echo "$perct%"
if [ $perct -gt 60 ] ; then
echo "Ram usage: $perct is above 60%" | mail -s "Critical Ram Usage" "example#example.com"
fi
#end
From this point , what can i do to improve my code ?

Bash script to find filesystem usage

EDIT: Working script below
I have used this site MANY times to get answers, but I am a little stumped with this.
I am tasked with writing a script, in bash, to log into roughly 2000 Unix servers (Solaris, AIX, Linux) and check the size of OS filesystems, most notable /var /usr /opt.
I have set some variables, which may be where I am going wrong right off the bat.
1.) First I am connecting to another server that has a list of all hosts in the infrastructure. Then I parse this data with some sed commands to get a list I can use properly
1.) Then I do a ping test, to see if the server is alive. If the server is decom. The idea behind this, is if the server is not pingable, I don't want it being reported on, or any attempt to be made to connect to it, as it is just wasting time. I feel I am doing this wrong, but don't know how to do it corectly (a re-occurring theme you will here in this post lol)
If any FS is over 80% mark, then it should output to a text file with the servername, filesystem, size on one line <== very important for me
If the FS is under 80% full, then I don't want it in my output, it can me omitted completely.
I have created something that I will post below, and am hoping to get some help in figuring out where I am going wrong. I am very new to bash scripting, but have experience as a Unix admin (i have never been good at scripting).
Can anyone provide some direction and teach me where I am going wrong?
I will upload my script that i can confirm is working hopefully tomorrow. thanks everyone for your input in this!
Here is my "disk usage" linux script, i hope that help you.
#!/bin/sh
df -H | awk '{ print $5 " " $6 }' | while read output;
do
echo $output
usep=$(echo $output | awk '{ print $1}' | cut -d'%' -f1 )
partition=$(echo $output | awk '{ print $2 }' )
if [ $usep -ge 90 ]; then
echo "Running out of space \"$partition ($usep%)\" on $(hostname) as on $(date)" |
mail -s "Warning! There is no space on the disk: $usep%" root#domain.com
fi
done
Some trouble is here:
ping -c 1 -W 3 $i > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "$i is offline" >> $LOG
fi
You need a continue statement inside that if. Your program isn't really treating non-pingable hosts differently, just logging they're not pingable.
Okay, now I'm looking a little deeper, and there's more naive stuff in here. These shouldn't work:
SOLVARFS=$(df -h /var |cut -f5 |grep -v capacity |awk '{print $5}')
SOLUSRFS=$(df -h /usr |cut -f5 |grep -v capacity |awk '{print $5}')
SOLOPTFS=$(df -h /opt |cut -f5 |grep -v capacity |awk '{print $5}')
etc...
The problem with these lines is, the command substitution gets assigned to the variables before the ssh session happens. So the content of each variable is the command's result on your local system, not the command itself. Since you're doing command substitution around your ssh calls, it might well work just to rewrite these lines as (note the backslash escapes on $5):
SOLVARFS="df -h /var |cut -f5 |grep -v capacity |awk '{print \$5}'"
SOLUSRFS="df -h /usr |cut -f5 |grep -v capacity |awk '{print \$5}'"
SOLOPTFS="df -h /opt |cut -f5 |grep -v capacity |awk '{print \$5}'"
etc...
The part where you're contacting another server has some more stuff to correct. You don't need three if statements per server, and there's no reason to echo anything to /dev/null. Here's a rewrite for the SunOS section. For each directory you're checking, it outputs the host name, the command name (so you can see which dir was being checked), and the result:
if [[ $UNAME = "SunOS" ]]; then
for SSH_COMMAND in SOLVARFS SOLUSRFS SOLOPTFS ; do
RESULT=`ssh -o PasswordAuthentication=no -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=2 GSSAPIAuthentication=no -q $i ${!SSH_COMMAND}`
if ["$RESULT" -gt 80] ; do
echo "$i, $SSH_COMMAND, $RESULT" >> $LOG
fi
done
fi
Note that the ${!BLAH} construction is variable indirection. "Give me the contents of the variable named by BLAH".
Your original script does a bunch of things less-than-optimally. Rather than running an almost-identical block of code for each filesystem and each operating system, the thing to do would be to record the differences in a way that a SINGLE piece of code can iterate over all your objects, adapting as required.
Here's my take on this. Commands should appear ONCE, but
they get run multiple times by loops, and
they get run multiple ways using arrays.
The following script passes lint checks, but obviously this is untested, as I don't have your environment to test in.
You might still want to think about how your logging and notifications work.
#!/bin/bash
# Assign temp file, remove it automatically upon successful exit.
tmpfile=$(mktemp /tmp/${0##*/}.XXXX)
trap "rm '$tmpfile'" 0
#NOW=$(date +"%Y-%m-%d-%T")
NOW=$(date +"%F")
LOG=/usr/scripts/disk_usage/Unix_df_issues-$NOW.txt
printf '' > "$LOG"
# Use variables to refer to commonly accessed files. If you change a name, just do it once.
rawhostlist=all_vms.txt
host_os=${rawhostlist}_OS
# Commonly-used options need only be declared once. Use an array for easier management.
declare -a ssh_opts=()
ssh_opts+=(-o PasswordAuthentication=no)
ssh_opts+=(-o BatchMode=yes)
ssh_opts+=(-o StrictHostKeyChecking=no) # Eliminate prompts on new hosts
ssh_opts+=(-o ConnectTimeout=2) # This should make your `ping` unnecessary.
ssh_opts+=(-o GSSAPIAuthentication=no) # This is default. Do we really need it?
# Note: Associative arrays require Bash 4.x.
declare -A df_opts=(
[SunOS]="-h"
[Linux]="-hP"
[AIX]=""
)
declare -A df_column=(
[SunOS]=5
[Linux]=5
[AIX]=4
)
# Fetch host list from configserver, stripping /^adm/ on the remote end.
ssh "${ssh_opts[#]}" -q configserver "sed 's/^adm//' /reports/*/HOSTNAME" > "$rawhostlist"
# Confirm that our host_os cache is up to date and process any missing hosts.
awk '
NR==FNR { h[$1]; next } # Add everything in rawhostlist to an array...
{ delete h[$1] } # Then remove any entries that exist in host_os.
END {
for (i in h) print i # And print whatever remains.
}' "$rawhostlist" "$host_os" |
while read h; do
printf '%s\t%s\n' "$h" $(ssh "$h" "${ssh_opts[#]}" -q uname -s)
done >> "$host_os"
# Next, step through the host list and collect data.
while read host os; do
ssh "${ssh_opts[#]}" "$host" df "${df_opts[$os]}" /var /usr /opt |
awk -v column="${df_column[$os]}" -v host="$host" 'NR>1 { print host,$1,$column }'
)
done < "$host_os" > "$tmpfile"
# Now that we have all our data, check for warning/critical levels.
while read host filesystem usage; do
if [ "$usage" -gt 80 ]; then
status="CRITICAL"
elif [ "$usage" -gt 70 ]; then
status="WARNING"
else
continue
fi
# Log our results to our log file, AND send them to stderr.
printf "[%s] %s: %s:%s at %d%%\n" "$(date +"%F %T")" "$status" "$host" "$filesystem" "$usage" | tee -a "$LOG" >&2
done < "$tmpfile"
# Email and record our results.
if [ -s "$LOG" ]; then
mail -s "Daily Unix /var Report - $NOW" unixsystems#examplle.com < "$LOG"
mv "$LOG" /var/log/vm_reports/
fi
Consider this example code. If you like the way it looks, your next task is to debug it, or open new questions for parts that you're having trouble debugging. :-)

Check free disk space for current partition in bash

I am writing an installer in bash. The user will go to the target directory and runs the install script, so the first action should be to check that there is enough space. I know that df will report all file systems, but I was wondering if there was a way to get the free space just for the partition that the target directory is on.
Edit - the answer I came up with
df $PWD | awk '/[0-9]%/{print $(NF-2)}'
Slightly odd because df seems to format its output to fit the terminal, so with a long mount point name the output is shifted down a line
Yes:
df -k .
for the current directory.
df -k /some/dir
if you want to check a specific directory.
You might also want to check out the stat(1) command if your system has it. You can specify output formats to make it easier for your script to parse. Here's a little example:
$ echo $(($(stat -f --format="%a*%S" .)))
df command : Report file system disk space usage
du command : Estimate file space usage
Type df -h or df -k to list free disk space:
$ df -h
OR
$ df -k
du shows how much space one or more files or directories is using:
$ du -sh
The -s option summarizes the space a directory is using and -h option provides Human-readable output.
I think this should be a comment or an edit to ThinkingMedia's answer on this very question (Check free disk space for current partition in bash), but I am not allowed to comment (not enough rep) and my edit has been rejected (reason: "this should be a comment or an answer").
So please, powers of the SO universe, don't damn me for repeating and fixing someone else's "answer". But someone on the internet was wrong!™ and they wouldn't let me fix it.
The code
df --output=avail -h "$PWD" | sed '1d;s/[^0-9]//g'
has a substantial flaw:
Yes, it will output 50G free as 50 -- but it will also output 5.0M free as 50 or 3.4G free as 34 or 15K free as 15.
To create a script with the purpose of checking for a certain amount of free disk space you have to know the unit you're checking against. Remove it (as sed does in the example above) the numbers don't make sense anymore.
If you actually want it to work, you will have to do something like:
FREE=`df -k --output=avail "$PWD" | tail -n1` # df -k not df -h
if [[ $FREE -lt 10485760 ]]; then # 10G = 10*1024*1024k
# less than 10GBs free!
fi;
Also for an installer to df -k $INSTALL_TARGET_DIRECTORY might make more sense than df -k "$PWD".
Finally, please note that the --output flag is not available in every version of df / linux.
df --output=avail -B 1 "$PWD" |tail -n 1
you get size in bytes this way.
A complete example for someone who may want to use this to monitor a mount point on a server. This example will check if /var/spool is under 5G and email the person :
#!/bin/bash
# -----------------------------------------------------------------------------------------
# SUMMARY: Check if MOUNT is under certain quota, mail us if this is the case
# DETAILS: If under 5G we have it alert us via email. blah blah
# -----------------------------------------------------------------------------------------
# CRON: 0 0,4,8,12,16 * * * /var/www/httpd-config/server_scripts/clear_root_spool_log.bash
MOUNTP=/var/spool # mount drive to check
LIMITSIZE=5485760 # 5G = 10*1024*1024k # limit size in GB (FLOOR QUOTA)
FREE=$(df -k --output=avail "$MOUNTP" | tail -n1) # df -k not df -h
LOG=/tmp/log-$(basename ${0}).log
MAILCMD=mail
EMAILIDS="dude#wheres.mycar"
MAILMESSAGE=/tmp/tmp-$(basename ${0})
# -----------------------------------------------------------------------------------------
function email_on_failure(){
sMess="$1"
echo "" >$MAILMESSAGE
echo "Hostname: $(hostname)" >>$MAILMESSAGE
echo "Date & Time: $(date)" >>$MAILMESSAGE
# Email letter formation here:
echo -e "\n[ $(date +%Y%m%d_%H%M%S%Z) ] Current Status:\n\n" >>$MAILMESSAGE
cat $sMess >>$MAILMESSAGE
echo "" >>$MAILMESSAGE
echo "*** This email generated by $(basename $0) shell script ***" >>$MAILMESSAGE
echo "*** Please don't reply this email, this is just notification email ***" >>$MAILMESSAGE
# sending email (need to have an email client set up or sendmail)
$MAILCMD -s "Urgent MAIL Alert For $(hostname) AWS Server" "$EMAILIDS" < $MAILMESSAGE
[[ -f $MAILMESSAGE ]] && rm -f $MAILMESSAGE
}
# -----------------------------------------------------------------------------------------
if [[ $FREE -lt $LIMITSIZE ]]; then
echo "Writing to $LOG"
echo "MAIL ERROR: Less than $((($FREE/1000))) MB free (QUOTA) on $MOUNTP!" | tee ${LOG}
echo -e "\nPotential Files To Delete:" | tee -a ${LOG}
find $MOUNTP -xdev -type f -size +500M -exec du -sh {} ';' | sort -rh | head -n20 | tee -a ${LOG}
email_on_failure ${LOG}
else
echo "Currently $(((($FREE-$LIMITSIZE)/1000))) MB of QUOTA available of on $MOUNTP. "
fi
This is one of those questions where everyone has their favorite approach, but since I have returned to this page a few times over the years I will share one of my solutions (inspired by others here).
DISK_SIZE_TOTAL=$(df -kh . | tail -n1 | awk '{print $2}')
DISK_SIZE_FREE=$(df -kh . | tail -n1 | awk '{print $4}')
DISK_PERCENT_USED=$(df -kh . | tail -n1 | awk '{print $5}')
Since it's just using df and pulling row/columns via awk it should be fairly portable.
Then you can use this in a script, like maybe:
"${DISK_SIZE_FREE}" available out of "${DISK_SIZE_TOTAL}" total ("${DISK_PERCENT_USED}" used).
Example: https://github.com/littlebizzy/slickstack/blob/master/bash/ss-install.txt
The final result looks like this:
10GB available out of 20GB total (50% used).
To know the usage of the specific directory in GB's or TB's in linux the command is,
df -h /dir/inner_dir/
or
df -sh /dir/inner_dir/
and to know the usage of the specific directory in bits in linux the command is,
df-k /dir/inner_dir/
Type in the command shell:
df -h
or
df -m
or
df -k
It will show the list of free disk spaces for each mount point.
You can show/view single column also.
Type:
df -m |awk '{print $3}'
Note: Here 3 is the column number. You can choose which column you need.

Bash monitor disk usage

I bought a NAS box which has a cut down version of debian on it.
It ran out of space the other day and I did not realise. I am basically wanting to write a bash script that will alert me whenever the disk gets over 90% full.
Is anyone aware of a script that will do this or give me some advice on writing one?
#!/bin/bash
source /etc/profile
# Device to check
devname="/dev/sdb1"
let p=`df -k $devname | grep -v ^File | awk '{printf ("%i",$3*100 / $2); }'`
if [ $p -ge 90 ]
then
df -h $devname | mail -s "Low on space" my#email.com
fi
Crontab this to run however often you want an alert
EDIT: For multiple disks
#!/bin/bash
source /etc/profile
# Devices to check
devnames="/dev/sdb1 /dev/sda1"
for devname in $devnames
do
let p=`df -k $devname | grep -v ^File | awk '{printf ("%i",$3*100 / $2); }'`
if [ $p -ge 90 ]
then
df -h $devname | mail -s "$devname is low on space" my#email.com
fi
done
I tried to use Erik's answer but had issues with devices having long names which wraps the numbers and causes script to fail, also the math looked wrong to me and didn't match the percentages reported by df itself.
Here's an update to his script:
#!/bin/bash
source /etc/profile
# Devices to check
devnames="/dev/sda1 /dev/md1 /dev/mapper/vg1-mysqldisk1 /dev/mapper/vg4-ctsshare1 /dev/mapper/vg2-jbossdisk1 /dev/mapper/vg5-ctsarchive1 /dev/mapper/vg3-muledisk1"
for devname in $devnames
do
let p=`df -Pk $devname | grep -v ^File | awk '{printf ("%i", $5) }'`
if [ $p -ge 70 ]
then
df -h $devname | mail -s "$devname is low on space" my#email.com
fi
done
Key changes are changed df -k to df -Pk to avoid line wrapping and simplified the awk to use pre-calc'd percent instead of recalcing.
You could also use Monit for this kind of job. It's a "free open source utility for managing and monitoring, processes, programs, files, directories and filesystems on a UNIX system".
Based on #Erik answer, here is my version with variables :
#!/bin/bash
DEVNAMES="/ /home"
THRESHOLD=80
EMAIL=you#email.com
host=$(hostname)
for devname in $DEVNAMES
do
current=$(df $devname | grep / | awk '{ print $5}' | sed 's/%//g')
if [ "$current" -gt "$THRESHOLD" ] ; then
mail -s "Disk space alert on $host" "$EMAIL" << EOF
WARNING: partition $devname on $host is $current% !!
To list big files (>100Mo) :
find $devname -xdev -type f -size +100M
EOF
fi
done
And if you do not have the mail command on your server, you can send email via SMPT with swaks :
swaks --from "$EMAIL" --to "$EMAIL" --server "TheServer" --auth LOGIN --auth-user "TheUser" --auth-password "ThePasswrd" --h-Subject "Disk space alert on $host" --body - << EOF
#!/bin/bash
DEVNAMES=$(df --output=source | grep ^/dev)
THRESHOLD=90
EMAIL=your#email
HOST=$(hostname)
for devname in $DEVNAMES
do
current=$(df $devname | awk 'NR>1 {printf "%i",$5}')
[ "$current" -gt "$THRESHOLD" ] && warn="WARNING: partition $devname on $HOST is $current% !! \n$warn"
done
[ "$warn" ] && echo -e "$warn" | mail -s "Disk space alert on $HOST" $EMAIL
Based on previous answers, here's my version with following changes:
Automatically checks all mounted devices
Sends only one mail per check, regardless of how many devices are over the threshold
Code generally tidied up

Is there a shell script that can monitor partition usage?

When I used to use cPanel it would send me an email when any partitions were approaching full. Is there a script out there that monitors df output that I can put in a cronjob?
Thanks
don't know if there's already one, but it's not too hard to write. Just put this into your crontab:
df | awk 'NR>1 && $5>80 {print $1 " " $5}'
You should replace 80 with the threshold (percent used) you want to be alerted on. If will mail you the df output for all partitions that cross that usage level.
It's way overkill for this application, but Nagios monitors disk usage and can email you alerts.
Nagios does this (and pretty much everything else). If you're setting up a server, it's a good thing to set up. If this is just for personal use, Nagios is probably overkill.
Try,
# cat partchek.sh
#!/bin/bash
a=$(/bin/df -h | grep /tmp |awk '{print $5}' | awk -F% '{print $1}')
if [ $a -ge 90 ] // if /tmp full greater than or equal to 90%
then
/bin/mail -s "Kindly check the server: `hostname -i` 's /tmp Partition, Its almost full" your#emailid.here
fi
Add below line in crontab:
* * * * * /bin/sh /path/for/partchek.sh > /dev/null 2>&1
(This is just an example for /tmp, you can edit for "/" and which you require)
Thanks.

Resources