Bash Script for automatically downloading, copying and updating LatLong geo location files from Digital Element

Problem this snippet solves:

This is a script that automatically downloads the latest geolocation database file from Digital Element, copies it to a list of previously defined LTMs and runs the geoip_update_data command.

You need to place this script in a directory and also create a simple text file where the script will read the LTM name and ip information from. The format of the file should follow the simple rule "ltm_name ltm_ip", with spaces or tabs delimiting the two columns, like the below example. The script is DNS agnostic so it will use the names as reference and the IPs to connect to.

la01ltm1 10.10.1.7

The script collects some information prior to executing and validates reachability of all the devices on file. It uses sshpass to connect to but you can change to SSH keys if preferred.

Code :

#!/bin/bash
echo "

Starting GeoLocation Update Script...
"
#read
function check_update {
#checks installed versions on F5 and compares with most recent file
echo "
Checking for updates and comparing with LTM $LTM_CHECKED...
"
GEOFILENAME_ONLINE_DATE=(`echo $GEOFILENAME | sed -n 's/.*-\([^.]*\).*/\1/p'`)
GEOFILENAME_LOCAL_DATE=(`sshpass -p '$LTM_PASSWORD' ssh -o StrictHostKeyChecking=no $LTM_USERNAME@$LTM_CHECKED geoip_lookup -f /shared/GeoIP/F5GeoIPCity2.dat 65.61.115.197 | grep version | sed -n 's/.* \(.*\) Build.*/\1/p'`)
echo "Current version date = $GEOFILENAME_LOCAL_DATE
Available version date = $GEOFILENAME_ONLINE_DATE
"
if [ $GEOFILENAME_ONLINE_DATE -gt $GEOFILENAME_LOCAL_DATE ]; then
echo "Update required. Hit enter to run script...
"
read
else
echo "Update not required. Exiting...
"
exit
fi
}
function default_option {
    #Presents the user a menu with the preconfigured options and gives them the option to change parameters
DIRECTORY=Scripts/GeoUpdate/Images
DEST_DIRECTORY=/shared/images
date=*
USERNAME=root
DEVICE_FILE=devices_adv_geo.txt
   echo -ne "
These are the default configs for the script. 
1 - Source Dir: $DIRECTORY
2 - Dest. Dir: $DEST_DIRECTORY
3 - Date: Most Recent File
4 - Username: $USERNAME
5 - Source device file: $DEVICE_FILE

Do you want to change? [Y/N - ENTER for NO] "
read -n 1 opt # waits for a y or n from the user and stores in $opt - "-n 1" means wait for one digit and don't wait for enter
#opt=n
if [ "$opt" = "y" ]; then #result if $opt is y
#assigns variable values or defaults
if [ -z $1 ]; then # if no parameters have been put in the command line then run the questions
echo -ne "
Enter source directory (Enter for default: $DIRECTORY): "
read DIRECTORY # reads source directory and stores in $DIRECTORY
if [ -z $DIRECTORY ]; then #if no input from user then set $DIRECTORY to default
DIRECTORY=Scripts/GeoUpdate/Images
fi
echo -ne "Enter destination directory (Enter for default: $DEST_DIRECTORY): "
read DEST_DIRECTORY
if [ -z $DEST_DIRECTORY ]; then 
DEST_DIRECTORY=/shared/images
fi
echo -ne "Enter date for Geo Files in format YYYYMMDD (Hit ENTER to use most recent file copied to $DIRECTORY): "
read date
if [ -z $date ]; then 
date=*
fi
echo -ne "Enter username (default $USERNAME): "
read USERNAME
if [ -z $USERNAME ]; then 
USERNAME=root
else 
echo -ne "Enter password for $USERNAME: "
read PASSWORD
fi
echo -ne "Enter device file (default $DEVICE_FILE). Device file must contain server name and ip, separated by a space, one server per line: "
read DEVICE_FILE
if [ -z $DEVICE_FILE ]; then 
DEVICE_FILE=devices_adv_geo.txt
fi
fi
elif [[ "$opt" = "n" || "$opt" = "" ]]; then #in case user doesn't want to change parameters, set all variables to default
echo "

Running Script with Default configs...
"
else #in case user types anything other than y or n, presents an error message and prints the menu again
echo "
Bad Option. Type y or n"
#read
#printf "\033c" 
default_option
fi
}
function read_usernames {
echo "
Collecting credentials...
"
echo -ne "Enter Digital Element username [default blank]: "
read DE_USERNAME
if [ -z $DE_USERNAME ]; then 
DE_USERNAME=""
fi
echo -ne "Enter Digital Element password [default blank]: "
read -s DE_PASSWORD
if [ -z $DE_PASSWORD ]; then 
DE_PASSWORD=""
fi
echo -ne "
Enter LTM username [default blank]: "
read LTM_USERNAME
if [ -z $LTM_USERNAME ]; then
LTM_USERNAME=""
fi
echo -ne "Enter LTM password [default blank]: "
read -s LTM_PASSWORD
if [ -z $LTM_PASSWORD ]; then 
LTM_PASSWORD=""
fi
LTM_CHECKED=(`head -1 $DEVICE_FILE | awk '{print $1}'`)
echo -ne "
Enter LTM to check with [default first device on file $DEVICE_FILE - $LTM_CHECKED]: "
read LTM_CHECKED
if [ -z $LTM_CHECKED ]; then 
LTM_CHECKED=(`head -1 $DEVICE_FILE | awk '{print $1}'`)
fi
}
default_option
read_usernames
GEOFILENAME=(`wget -qO- --user=$DE_USERNAME --http-password=$DE_PASSWORD https://portal.digitalelement.com/downloads/F5Edge/files/ | grep City2 | sed -n 's/[^"]*"\.\/\([^"]*\).*/\1/p'`)
check_update
echo "Downloading Advanced Geo File from Digital Element..."
function download_geo_file {
  if [ -s "$DIRECTORY/$GEOFILENAME" ]; then
LOCALGEOFILESIZE=(`ls -la $DIRECTORY/$GEOFILENAME | awk {'print $5'}`)
LOCALGEOFILESIZE=(`echo $[LOCALGEOFILESIZE/1024/1024]`)
REMOTEGEOFILESIZE=(`wget -qO- --user=$DE_USERNAME --http-password=$DE_PASSWORD https://portal.digitalelement.com/downloads/F5Edge/files/ | grep City2 | sed -n 's/.*[^\.]'$GEOFILENAME'[^(]*(\([0-9]*\)M).*/\1/p'`)
echo -ne "

File with same name exists on target directory.

File name: $GEOFILENAME

File size on local directory is $LOCALGEOFILESIZE MB.
File size on Digital Element is $REMOTEGEOFILESIZE MB

Do you want to re-download and overwrite it [y/n - ENTER for YES]?"
read -n 1 opt
if [[ "$opt" = "y"  || "$opt" = "Y" || "$opt" = "" ]]; then 
wget -q --show-progress --user=$DE_USERNAME --http-password=$DE_PASSWORD https://portal.digitalelement.com/downloads/F5Edge/files/$GEOFILENAME -O "$DIRECTORY/$GEOFILENAME"
echo "
done
"
else 
echo "
Skypping file download...
"
return
fi
  fi
}
download_geo_file

FILE_ID=(City2) #creates an array with the 5 types of files to be transferred/updated on the F5 appliances
deviceip=(`cat $DEVICE_FILE | awk '{print $2}'`) #reads device file and prints only second column to array $deviceip
devicename=(`cat $DEVICE_FILE | awk '{print $1}'`) #reads device file and prints only first column to array $devicename
len=${#deviceip[*]} #creates variable $len with total number of lines read on file
fil=${#FILE_ID[*]} #creates variable $fil with total number of file types existing - always 5 unless otherwise stated
function display {
 # displays the list of devices and IPs and does a small sanity check (ping)
 i=0 #auxiliar variable to while command
l=1 #will transform array zero in 'item number 1'
echo "
LIST OF DEVICES:

Checking hosts availability...
"
echo "+----------+-------------------------------+----------------+------------+"
echo "| Device   |  IP Address                   | Name           | Available? |"
echo "|----------+-------------------------------+----------------+------------+"
while [ $i -lt $len ]; do #executes while auxiliar variable is lower than the number of devices read previously
ping -c 2 -t 1 -i 0.2 ${deviceip[$i]} > /dev/null #ping on device
avail=$? #throws ping status ($?) into variable $avail
echo -ne "| $l  " $'\t' "  |  ${deviceip[$i]} " $'\t' $'\t' "  | ${devicename[$i]}"; if [ $avail == 0 ]; then #prints number of device (array position 0 will be device 1, then prints the ip then prints the name then
echo -ne $'\t' "   |  "; echo -ne '\033[1;32m' "UP" '\033[0m'; echo "      |"                         # if status comes out 0 means that host is UP, prints UP in green, DOWN in red
else echo -ne $'\t' "   |  "; echo -ne '\033[1;31m' "DOWN" '\033[0m'; echo "    |"
fi
let i++
let l++
done
echo "+----------+-------------------------------+----------------+------------+"
echo "
LIST OF FILES:
"
j=0 #auxiliar variable to
 # runs a ls sorted by time on the source directory, looking for one of the four file types. If user provided a specific date, also looks for it
 # otherwise it will display the most recent file. head 1 prints only one file and xargs basename will strip out the directory from the ls output
while [ $j -lt $fil ]; do 
filename=(`ls -t $DIRECTORY/*${FILE_ID[$j]}*$date* | head -1 | xargs -n1 basename`)                   
echo "${FILE_ID[$j]} File: $filename "
let j++
done
}
function filecopy {
 #copies the file via scp to the host
 filename=(`ls -t $DIRECTORY/*$1*$date* | head -1 | xargs -n1 basename`) #runs the same ls as before to assign the filename to the variable. $1 is the parameter passed whe the function was called
echo -ne '\033[1;36m' "
*** Copying $1 to ${devicename[$i]} ***
" '\033[0m'; #informs the user about the type of file being copied and to which host
echo -ne '\033[1;19m' "
Executing Command: scp $DIRECTORY/$filename $LTM_USERNAME@${deviceip[$i]}:$DEST_DIRECTORY/$filename
" '\033[0m'; #informs the user the specific command
sshpass -p '$LTM_PASSWORD' scp $DIRECTORY/$filename $LTM_USERNAME@${deviceip[$i]}:$DEST_DIRECTORY/$filename #copies using source directory specified in beginning, filename just recently listed and to device ip sent as a parameter when the function was called
status=${?} #saves copy status in a variable
if [ $status != 0 ]; then 
echo "Transfer failed" #error message in case the status is different then zero
fi
}
function run_update {
filename=(`ls -t $DIRECTORY/*$1*$date* | head -1 | xargs -n1 basename`)
echo -ne '\033[1;36m' "

*** Executing $1 update on ${devicename[$i]} ***" '\033[0m';
        LOGDIR=(`echo $DIRECTORY | sed -n 's/\/Images/\/log/p'`)
        mkdir -p $LOGDIR
sshpass -p '$LTM_PASSWORD' ssh -o StrictHostKeyChecking=no -v -E $LOGDIR/${devicename[$i]}_file_${filename}_copy_$(date +"%m_%d_%Y").txt $LTM_USERNAME@${deviceip[$i]} geoip_update_data -f $DEST_DIRECTORY/$filename
status=${?}
if [ $status == 0 ]; then
printcmd="ssh -v -E /tmp/${deviceip[$i]}_file_${filename}_copy_$(date +"%m_%d_%Y").txt $LTM_USERNAME@${deviceip[$i]} geoip_update_data -f $DEST_DIRECTORY/$filename" #saves the command -v verbose, -E creates a file log
echo -ne '\033[1;19m' "

Running:"; echo $printcmd | awk {'print $1 " " $5 " " $6 " " $7 " " $8'}
echo -ne "
" '\033[0m'
awk '{ $1=""; print $0 }' $LOGDIR/${devicename[$i]}_file_${filename}_copy_$(date +"%m_%d_%Y").txt | grep Connecting | sort -u
awk '{ $1=""; print $0 }' $LOGDIR/${devicename[$i]}_file_${filename}_copy_$(date +"%m_%d_%Y").txt | grep Connection | sort -u
awk '{ $1=""; print $0 }' $LOGDIR/${devicename[$i]}_file_${filename}_copy_$(date +"%m_%d_%Y").txt | grep 'Sending command' | sort -u
echo " Command Executed Successfully"
GEOFILENAME_TESTED_DATE=(`sshpass -p '$LTM_PASSWORD' ssh $LTM_USERNAME@${deviceip[$i]} geoip_lookup -f /shared/GeoIP/F5GeoIPCity2.dat 65.61.115.197 | grep version | sed -n 's/.* \(.*\) Build.*/\1/p'`)
if [ $GEOFILENAME_ONLINE_DATE -eq $GEOFILENAME_TESTED_DATE ]; then
VERIFY_PASS="UP TO DATE"
else
VERIFY_PASS="OUTDATED FILE!!!!"
fi
echo " 

Verifying database date installed on ${devicename[$i]} : $VERIFY_PASS"
rm $LOGDIR/${devicename[$i]}_file_${filename}_copy_$(date +"%m_%d_%Y").txt
else echo "Command failed - Check Log file: $LOGDIR/${devicename[$i]}_file_$1_copy.txt"
fi
}
display
echo "
Hit Enter to start Script, Ctrl-C to stop"
read
i=0 # not a simple auxiliar variable. this "i" will be called by the two functions filecopy and run_update. it will determine IN THE FUNCTION what is the array position the command should be called, although it's not passed as parameter, it can be used anytime in the script.
while [ $i -lt $len ]; do #it will run for as many IPs are in the file
k=0
while [ $k -lt $fil ]; do #it will run for as many types of files exist. (Remember, so far this number is fixed in 4) This internal while will run 4 times for each ip, and the number of times for each ip will be determined by the outer while
echo -ne '\033[1;31m' "

============================  Processing file ${FILE_ID[$k]} on ${devicename[$i]}  =========================================================
" '\033[0m';
filecopy ${FILE_ID[$k]} #pass the type of file as a parameter. function will see ${FILE_ID[$k]} as $1. Will use i to determine what IP will be treated this time
run_update ${FILE_ID[$k]} 
let k++
done
let i++
done
echo -ne '\033[1;19m'"

*************************** Script complete. For full details check log file *******************************************

" '\033[0m'

Tested this on version:

11.6
Published Jul 05, 2016
Version 1.0

Was this article helpful?

No CommentsBe the first to comment