Email Alerts when service or server automatically comes up - bash

I am working on a bash script that helps to ping and get the network interface level status of the host machines and services.
This script will send a email alerts in case of failure.
#!/bin/bash
HOSTS="192.168.8.200"
COUNT=4
for myHost in $HOSTS
do
count=$(ping -c $COUNT $myHost | grep 'received' | awk -F',' '{ print $2 }' | awk '{ print $1 }')
if [ $count -eq 0 ]; then
# 100% failed
echo -e "HOST:$myHost is down (ping failed) at $(date)" | mailx -A gmail -s “Mail subject” anymail#anydomain.com
fi
done
This works fine.
But need help to get a one single email alert when host automatically comes up (ping success).

You need to save the state of the host (up/down) during the calls of your script.
if the host is "up" and the former state was "down" then you need to send an email.
You can just write the result of the "check command" to a file in /tmp/
if the check returns that the server is up you read the content of the file. if the state is "down" in the file, then send an email an write "up" to the file.
on the next check if the server is up, there will be no additional email sent, because the server was also "up" before.
#!/bin/bash
HOSTS="192.168.8.200 192.168.8.201 192.168.122.1"
COUNT=4
STATE="/tmp/ping_state.txt"
for myHost in $HOSTS
do
count=$(ping -c $COUNT $myHost | grep 'received' | awk -F',' '{ print $2 }' | awk '{ print $1 }')
if [ $count -eq 0 ]; then
# 100% failed
#echo -e "HOST:$myHost is down (ping failed) at $(date)" | mailx -A gmail -s “Mail subject” anymail#anydomain.com
echo "host $myHost down"
#delete all previous entries of that ip
sed -i "/$myHost/d" $STATE
#mark host as down
echo "$myHost - down" >> $STATE
else
CHECK=`grep "$myHost" $STATE | grep -o "down"`
if [ "$CHECK" = "down" ]; then
echo "host $myHost up again"
#insert email for host up here
fi
#delete all previous entries of that ip
sed -i "/$myHost/d" $STATE
echo "$myHost - up" >> $STATE
fi
done
for simple testing I just used an echo statement instead of sending an email.

Related

Sorting information and processing output with variables in Bash

I've received some helpful responses in the past and am hoping you can all help me out. I came across some weird behavior that I can't quite nail down. I'm processing configuration files for Cisco switches and want to generate output that lists the VLAN IP Addresses in a format that would show:
Vlan1: 172.31.200.1 255.255.255.0
Vlan10: 172.40.220.1 255.255.255.0
The "Vlan" would be captured in a variable and the IP/Mask is extracted using "sed" and it works for the most part. Occasionally though it refuses to populate the "vlan" variable even though it appears to work great for other configs.
If there's only one VLAN it just handles that one, if there's more than one it handles the additional ones. If the user selects (-v) it includes VLAN1 on the list there are multiple VLANs configured (otherwise it ignores VLAN1).
This input file appears broken (Filename 1.cfg):
!
interface Vlan1
ip address 172.29.96.100 255.255.255.0
!
ip default-gateway 172.29.96.1
ip http server
no ip http secure-server
!
This input file works fine (Filename 2.cfg):
!
interface Vlan1
ip address 172.31.200.111 255.255.255.0
no ip route-cache
!
ip default-gateway 172.31.200.1
ip http server
ip http secure-server
The output that I get is this:
Notice how the first one fails to include the "Vlan1" reference?
Here's my script:
#!/bin/bash
if [ -f getlan.log ];
then
rm getlan.log
fi
TempFile=getlan.log
verbose=0
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-v)
verbose=1
shift
;;
#*)
#exit
#shift
#;;
esac
done
#Start Processing all files
files=$( ls net )
for i in $files; do
#########################################################
# Collect Configured VLAN Interfaces and IP Information #
#########################################################
echo "-------- Configured VLAN Interfaces --------" >> ~/$TempFile
echo "" >> ~/$TempFile
if [ `grep "^interface Vlan" ~/net/$i | awk '{ print $2 }' | wc -l` -gt 1 ];
then
for g in `grep "^interface Vlan" ~/net/$i | awk '{ print $2 }'`;
do
if [ $g == "Vlan1" ];
then
if [ $verbose -gt 0 ];
then
echo "$g": `sed -n '/^interface '$g'/,/!/p' ~/net/$i | head -n 5 | grep -i "ip address" | awk '{ print $3, $4 }'` >> ~/$TempFile
fi
else
echo "$g": `sed -n '/^interface '$g'/,/^!/p' ~/net/$i | grep -i "ip address" | awk '{ print $3, $4 }'` >> ~/$TempFile
fi
done
echo "" >> ~/$TempFile
else
vlanid=`grep "^interface Vlan" ~/net/$i | awk '{ print $2 }'`
echo $vlanid: `sed -n '/^interface 'Vlan'/,/^!/p' ~/net/$i | grep -i "address" | awk '{ print $3, $4 }'` >> ~/$TempFile
echo "" >> ~/$TempFile
fi
done
It would be really great if this was more consistent. Thanks!
A best-practices approach might look more like:
#!/usr/bin/env bash
interface_re='^[[:space:]]*interface[[:space:]]+(.*)'
ip_re='^[[:space:]]*ip address (.*)'
process_file() {
local line interface
interface=
while IFS= read -r line; do
if [[ $line =~ $interface_re ]]; then
interface=${BASH_REMATCH[1]}
continue
fi
if [[ $interface ]] && [[ $line =~ $ip_re ]]; then
echo "${interface}: ${BASH_REMATCH[1]}"
fi
done
}
for file in net/*; do
process_file <"$file"
done
This can be tested as follows:
process_file <<'EOF'
!
interface Vlan1
ip address 172.29.96.100 255.255.255.0
!
ip default-gateway 172.29.96.1
ip http server
no ip http secure-server
!
!
interface Vlan1
ip address 172.31.200.111 255.255.255.0
no ip route-cache
!
ip default-gateway 172.31.200.1
ip http server
ip http secure-server
EOF
...which correctly identifies the ip address lines from both interface blocks, emitting:
Vlan1: 172.29.96.100 255.255.255.0
Vlan1: 172.31.200.111 255.255.255.0

Verification of Email Addresses from MX using Bash

I want to verify a batch of email addresses in a file (say .txt) using telnet to mx and declaring recipient then checking response.
I need a script that reads email addresses from a file, takes out host after '#', looks up MX, telnets(or mailcmd, whichever you prefer), does the deed, then outputs result in 2 files (valid and invalid)
YES I have searched before posting and a few answers were close but I am a noob at bash and modifying them to suit my needs was not possible for me.
Please help!
PS: Below is what I found but I don't know how to pass email addresses to it, and also it is fixed with a single MX.
#!/bin/bash
# check for valid usage
if [ x$1 = 'x' ]
then
echo "Usage: $0 <email address>"
exit 1
fi
# grabbing fields
user=`echo $1 | cut -f1 -d\#`
host=`echo $1 | cut -f2 -d\#`
mxhost=`host -t mx $host | cut -f7 -d\ `
len=`echo $mxhost | wc -c`
len=`expr $len - 2`
mxhost=`echo $mxhost | cut -b1 -$len`
# compose email commands
echo -ne "helo test.com\r\n" > mailcmd
echo -ne "mail from: test\#test.com\r\n" >> mailcmd
echo -ne "rcpt to: $1\r\n" >> mailcmd
echo -ne "quit\r\n" >> mailcmd
# check for mail results
mailresult=`cat mailcmd | nc $mxhost 25| grep ^550 | wc -c`
if [ $mailresult -eq 0 ]
then
echo $1 "is valid"
exit 0
else
echo $1 "is not valid"
exit 1
fi
# clean up
rm mailcmd

My script is sending non-stop

My shell script below is sending non-stop even if the parameters are not being met already:
MAX=85
EMAIL="my#email.com"
PART=sda1
USE=`df -h |grep $PART | awk '{ print $5 }' | cut -d'%' -f1`
if [ $USE -gt $MAX ]; then
echo "Percent used: $USE of /" | mail -s "Server is running out of disk space" $EMAIL
fi
This code is working on my system. Perhaps check to make sure that the output of dh is always adding up to an amount greater than 85. You may have more success not using the -h argument to dh and instead retrieving the actual number in bytes and operating on that instead.
#!/bin/sh
# Change this number accordingly as usual
MAX=100000000
EMAIL="my#email.com"
PART=sda1
USE=$( df | grep $PART | awk '{ print $3 }' )
if [ "$USE" -gt "$MAX" ]
then echo "Successful"
#echo "Percent used: $USE of /" | mail -s "Server is running out of disk space" $EMAIL
fi
Or for the percentage perhaps try POSIX shell string cutting:
#!/bin/sh
MAX=85
EMAIL="my#email.com"
PART=sda1
USE=$( df | grep $PART | awk '{ print $5 }' )
if [ "${USE%%%}" -gt "$MAX" ]
then echo "Successful"
#echo "Percent used: $USE of /" | mail -s "Server is running out of disk space" $EMAIL
fi
This server is running in AWS. I looked at the email's headers and saw a queer IP address. I traced the IP in AWS and found a replica of the original server I thought was sending out the mails. It was cloned by a now-defunct vendor. So, I just terminated that server and problem solved! :D
This is a very good community! The people are very gifted technically and are so whilling to share. Keep it up, guys! I appreciate all those who tried to help me. Thanks!

how to split a line into array in shell

below script is used for drop a mail while ping dropped in network
subject="Ping failed"
Email="test1#server.abc.com"
awk '{print $1}' < b.txt | while read ip;do
CNT=$(ping -c 1 $ip | grep 'received' | awk -F',' '{ print $2 }' | awk '{ print $1 }')
if [ $CNT -eq 0 ]; then
echo "Host : $ip is down (ping failed) at $(date)"| mail -s "$subject" $Email
fi
done
This script is working fine. Input file has the following content..
192.2.165.1 ttcn
192.3.4.23 dct
192.3.4.24 abc
I want to split lines of this file into 0 and 1 index form like array and mail format should be
Host : $ip ttcn is down (ping failed) at $(date)"
can anyone help me to get this?
To read a line into an array use read -a arr and then access the elements using ${arr[0]}, ${arr[1]} etc.
Also, you don't need to parse the output of ping to check if the host responded. Just use the exit status instead.
Here is the revised version:
while read -r -a arr
do
ip="${arr[0]}"
if ! ping -q -c 1 "$ip" > /dev/null
then
mail -s "$subject" "$email" <<< "Host $ip is down (ping failed) at $(date)"
fi
done < b.txt
Give multiple arguments to read, and each column will be read into the corresponding variable:
while read ip name;do
CNT=$(ping -c 1 $ip | awk -F',' '/received/ { split($2, a, " "); print a[1]}')
if [ $CNT -eq 0 ]; then
echo "Host : $ip $name is down (ping failed) at $(date)"| mail -s "$subject" $Email
fi
done < b.txt
you can use awk
echo "192.2.165.1 ttcn" | awk ' { split($0,a,"");ip=a[1]; print $ip}'

Bash ping status script

I've done the following script
HOSTS="ns1.server.com ns2.server.com"
SUBJECT="Host Down"
for myHost in $HOSTS
do
count=$(ping -c 10 $myHost | grep 'received' | awk -F',' '{ print $2 }' | awk '{
print $1 }')
if [ $count -eq 0 ]; then
echo "Host : $myHost is down (ping failed) at $(date)" | sendEmail -f email (email address removed) -u "$SUBJECT" etc etc
fi
done
Run via cron every 5 minutes however when a host is down I will receive and email every 5 minutes reflecting this. What i'd like is to add the function so that it only emails me when the status has changed. ie if it's down I don't want it to send any further updates until it's up.
I think something like this can help:
#!/bin/bash
HOSTS="ns1.server.com ns2.server.com"
HOSTS="123.123.1.1 ns1.server.com"
SUBJECT="Host Down"
ping_attempts=1
down_hosts=down_hosts.txt
for myHost in $HOSTS
do
count=$(ping -c $ping_attempts $myHost | awk -F, '/received/{print $2*1}')
echo $count
if [ $count -eq 0 ]; then
echo "$myHost is down"
if [ $(grep -c "$myHost" "$down_hosts") -eq 0 ]; then
echo "Host : $myHost is down (ping failed) at $(date)"
echo "$myHost" >> $down_hosts
fi
else
echo "$myHost is alive"
if [ $(grep -c "$myHost" "$down_hosts") -eq 1 ]; then
echo "Host : $myHost is up (ping ok) at $(date)"
sed -i "/$myHost/d" "$down_hosts"
fi
fi
done
There is a good point in the comments that you might want to use an infinite loop. But as you have asked for something different, here you go:
HOSTS="ns1.server.com ns2.server.com"
SUBJECT="Host Down"
PATH_STATUS='/yourfolder/hoststatus_' # For example can be located in /tmp.
for myHost in $HOSTS; do
count=$(ping -c 10 "$myHost" | grep 'received' | awk -F',' '{ print $2 }' | awk '{ print $1 }')
[[ -f "$PATH_STATUS$myHost"]] && prevStatus=$(cat "$PATH_STATUS$myHost") || prevStatus='unknown'
[[ $count == 0 ]] && curStatus='down' || curStatus='up'
if [[ $curStatus != $prevStatus ]]; then
echo "$curStatus" > "$PATH_STATUS$myHost"
echo "Host : $myHost is $curStatus at $(date)" | sendEmail
fi
done

Resources