- basics of init-script working
- basic shell script which enables virtualenv environment and calls traceroute.py at intervals - added stubs to only call webhook based on a specified latency - still need to store offline data in redis - coming soon
This commit is contained in:
parent
b2c2b63b1c
commit
ffd8158c25
4 changed files with 152 additions and 7 deletions
|
|
@ -7,6 +7,7 @@ Multi-source traceroute with geolocation information. Demo: [IP Address Lookup](
|
||||||
|
|
||||||
1. Install dependencies listed in requirements.txt file. Pip is recommended. You could also use virtualenv to keep things isolated.
|
1. Install dependencies listed in requirements.txt file. Pip is recommended. You could also use virtualenv to keep things isolated.
|
||||||
2. Save traceroute.py into a directory with its path stored in your PYTHONPATH environment variable. (if using virtualenv, copy it here)
|
2. Save traceroute.py into a directory with its path stored in your PYTHONPATH environment variable. (if using virtualenv, copy it here)
|
||||||
|
3. Install Redis-Server ( may change )
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
|
||||||
64
init-script/traceroute
Normal file
64
init-script/traceroute
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# traceroute.py daemon
|
||||||
|
# chkconfig: 345 20 80
|
||||||
|
# description: traceroute.py daemon
|
||||||
|
# processname: traceroute.py
|
||||||
|
|
||||||
|
DAEMON_PATH="/var/lib/python/traceroute"
|
||||||
|
|
||||||
|
DAEMON=traceroute.sh
|
||||||
|
DAEMONOPTS=""
|
||||||
|
|
||||||
|
NAME=traceroute.py
|
||||||
|
DESC="Traceroute Python Tool"
|
||||||
|
PIDFILE=/var/run/$NAME.pid
|
||||||
|
SCRIPTNAME=/etc/init.d/$NAME
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
printf "%-50s" "Starting $NAME..."
|
||||||
|
cd $DAEMON_PATH
|
||||||
|
PID=`$DAEMON $DAEMONOPTS > /dev/null 2>&1 & echo $!`
|
||||||
|
#echo "Saving PID" $PID " to " $PIDFILE
|
||||||
|
if [ -z $PID ]; then
|
||||||
|
printf "%s\n" "Fail"
|
||||||
|
else
|
||||||
|
echo $PID > $PIDFILE
|
||||||
|
printf "%s\n" "Ok"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
printf "%-50s" "Checking $NAME..."
|
||||||
|
if [ -f $PIDFILE ]; then
|
||||||
|
PID=`cat $PIDFILE`
|
||||||
|
if [ -z "`ps axf | grep ${PID} | grep -v grep`" ]; then
|
||||||
|
printf "%s\n" "Process dead but pidfile exists"
|
||||||
|
else
|
||||||
|
echo "Running"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "%s\n" "Service not running"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
printf "%-50s" "Stopping $NAME"
|
||||||
|
PID=`cat $PIDFILE`
|
||||||
|
cd $DAEMON_PATH
|
||||||
|
if [ -f $PIDFILE ]; then
|
||||||
|
kill -HUP $PID
|
||||||
|
printf "%s\n" "Ok"
|
||||||
|
rm -f $PIDFILE
|
||||||
|
else
|
||||||
|
printf "%s\n" "pidfile not found"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
restart)
|
||||||
|
$0 stop
|
||||||
|
$0 start
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {status|start|stop|restart}"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
19
init-script/traceroute.sh
Executable file
19
init-script/traceroute.sh
Executable file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
EXEC_DIR=/var/lib/python/traceroute
|
||||||
|
VIRTUALENV_DIR=/var/lib/python/traceroute/env
|
||||||
|
|
||||||
|
# Repeat every 5 seconds
|
||||||
|
INTERVAL=5 #
|
||||||
|
|
||||||
|
source $VIRTUALENV_DIR/bin/activate
|
||||||
|
|
||||||
|
cd $EXEC_DIR
|
||||||
|
|
||||||
|
# TODO add some logging later
|
||||||
|
|
||||||
|
while true;
|
||||||
|
do
|
||||||
|
python ./traceroute.py --ip_address=8.8.8.8 -c LO --webhook=http://localhost:8081/test;
|
||||||
|
sleep $INTERVAL;
|
||||||
|
done
|
||||||
|
|
@ -27,7 +27,7 @@ class Traceroute(object):
|
||||||
Multi-source traceroute instance.
|
Multi-source traceroute instance.
|
||||||
"""
|
"""
|
||||||
def __init__(self, ip_address, source=None, country="US", tmp_dir="/tmp",
|
def __init__(self, ip_address, source=None, country="US", tmp_dir="/tmp",
|
||||||
no_geo=False, timeout=120, debug=False):
|
no_geo=False, timeout=120, debug=False, max_latency=5):
|
||||||
super(Traceroute, self).__init__()
|
super(Traceroute, self).__init__()
|
||||||
self.ip_address = ip_address
|
self.ip_address = ip_address
|
||||||
self.source = source
|
self.source = source
|
||||||
|
|
@ -37,6 +37,9 @@ class Traceroute(object):
|
||||||
self.source = sources[country]
|
self.source = sources[country]
|
||||||
self.tmp_dir = tmp_dir
|
self.tmp_dir = tmp_dir
|
||||||
|
|
||||||
|
self.LATENCY_THRESHOLD = float(max_latency)
|
||||||
|
|
||||||
|
|
||||||
self.no_geo = no_geo
|
self.no_geo = no_geo
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
|
|
@ -44,6 +47,9 @@ class Traceroute(object):
|
||||||
self.hops = {}
|
self.hops = {}
|
||||||
self.country = country
|
self.country = country
|
||||||
|
|
||||||
|
# flag to determine if webhook alert is warranted
|
||||||
|
self.latency_exceeded = False
|
||||||
|
|
||||||
# Localhost Specific operations happen here
|
# Localhost Specific operations happen here
|
||||||
if self.country == 'LO':
|
if self.country == 'LO':
|
||||||
self.local_mode = True
|
self.local_mode = True
|
||||||
|
|
@ -59,6 +65,11 @@ class Traceroute(object):
|
||||||
self.__run_traceroute()
|
self.__run_traceroute()
|
||||||
self.probe_end = time.time() * 1000
|
self.probe_end = time.time() * 1000
|
||||||
|
|
||||||
|
def pingLatencyThresholdExceeded(self):
|
||||||
|
"""public method to query state of Traceroute calls"""
|
||||||
|
|
||||||
|
return self.latency_exceeded
|
||||||
|
|
||||||
def __run_traceroute(self):
|
def __run_traceroute(self):
|
||||||
"""
|
"""
|
||||||
Instead of running the actual traceroute command, we will fetch
|
Instead of running the actual traceroute command, we will fetch
|
||||||
|
|
@ -134,6 +145,7 @@ class Traceroute(object):
|
||||||
hop_element_pattern = '([\d\w.-]+)\s+\((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\)\s+(\d+\.\d+ ms)'
|
hop_element_pattern = '([\d\w.-]+)\s+\((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\)\s+(\d+\.\d+ ms)'
|
||||||
hp = re.compile(hop_element_pattern)
|
hp = re.compile(hop_element_pattern)
|
||||||
|
|
||||||
|
alertTriggered = False
|
||||||
for entry in traceroute.split('\n'):
|
for entry in traceroute.split('\n'):
|
||||||
entry = entry.strip()
|
entry = entry.strip()
|
||||||
result = re.match(hop_pattern,entry)
|
result = re.match(hop_pattern,entry)
|
||||||
|
|
@ -146,9 +158,17 @@ class Traceroute(object):
|
||||||
hop_hosts = re.findall(host_pattern, hop['hosts'])
|
hop_hosts = re.findall(host_pattern, hop['hosts'])
|
||||||
|
|
||||||
self.hops[hop_num] = []
|
self.hops[hop_num] = []
|
||||||
|
|
||||||
for host in hop_hosts:
|
for host in hop_hosts:
|
||||||
m = hp.search(host)
|
m = hp.search(host)
|
||||||
(hostname, ip, ping_time) = m.groups()
|
(hostname, ip, ping_time) = m.groups()
|
||||||
|
|
||||||
|
# Check ping time to see if it exceeds threshold. Once one is found, don't need any more info from other hops
|
||||||
|
if alertTriggered is False:
|
||||||
|
if self._exceeds_hop_latency(ping_time):
|
||||||
|
self.latency_exceeded = True
|
||||||
|
alertTriggered = True
|
||||||
|
|
||||||
if self.no_geo:
|
if self.no_geo:
|
||||||
self.hops[hop_num].append(
|
self.hops[hop_num].append(
|
||||||
{
|
{
|
||||||
|
|
@ -207,6 +227,15 @@ class Traceroute(object):
|
||||||
if 'latitude' in tmp_location and 'longitude' in tmp_location:
|
if 'latitude' in tmp_location and 'longitude' in tmp_location:
|
||||||
location = tmp_location
|
location = tmp_location
|
||||||
return location
|
return location
|
||||||
|
def _exceeds_hop_latency(self,ping_time):
|
||||||
|
"""return true if hop time exceeds specified latency threshold"""
|
||||||
|
# remote ' ms' from ping time
|
||||||
|
ping_as_float = float(ping_time.replace(" ms",""))
|
||||||
|
return ping_as_float >= self.LATENCY_THRESHOLD
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def execute_cmd(self, cmd):
|
def execute_cmd(self, cmd):
|
||||||
"""
|
"""
|
||||||
|
|
@ -360,6 +389,18 @@ def post_result(webhook_url, report, timeout=120):
|
||||||
"""
|
"""
|
||||||
return requests.post(webhook_url, data=json.dumps(report), timeout=timeout)
|
return requests.post(webhook_url, data=json.dumps(report), timeout=timeout)
|
||||||
|
|
||||||
|
def webhook_available(webhook_url):
|
||||||
|
"""
|
||||||
|
Function to check if a webhook host is responding.
|
||||||
|
Not 100% sure this will work...
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data = urllib.urlopen(webhook_url)
|
||||||
|
return True
|
||||||
|
except Exception,e:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
cmdparser = optparse.OptionParser("%prog --ip_address=IP_ADDRESS")
|
cmdparser = optparse.OptionParser("%prog --ip_address=IP_ADDRESS")
|
||||||
|
|
@ -394,6 +435,11 @@ def main():
|
||||||
"-w", "--webhook", type="string", default="",
|
"-w", "--webhook", type="string", default="",
|
||||||
help="Specify URL to POST report payload rather than stdout")
|
help="Specify URL to POST report payload rather than stdout")
|
||||||
|
|
||||||
|
cmdparser.add_option(
|
||||||
|
"--max_latency", type="int", default="5",
|
||||||
|
help="Maximum latency whereby the system will trigger the webhook ( if requested ). ")
|
||||||
|
|
||||||
|
|
||||||
options, _ = cmdparser.parse_args()
|
options, _ = cmdparser.parse_args()
|
||||||
json_file = open(options.json_file, "r").read()
|
json_file = open(options.json_file, "r").read()
|
||||||
sources = json.loads(json_file.replace("_IP_ADDRESS_", options.ip_address))
|
sources = json.loads(json_file.replace("_IP_ADDRESS_", options.ip_address))
|
||||||
|
|
@ -406,17 +452,32 @@ def main():
|
||||||
tmp_dir=options.tmp_dir,
|
tmp_dir=options.tmp_dir,
|
||||||
no_geo=options.no_geo,
|
no_geo=options.no_geo,
|
||||||
timeout=options.timeout,
|
timeout=options.timeout,
|
||||||
debug=options.debug)
|
debug=options.debug, max_latency = options.max_latency)
|
||||||
|
|
||||||
# pull complete report -> Hop data plus meta info about the network
|
# pull complete report -> Hop data plus meta info about the network
|
||||||
report = traceroute.get_report()
|
report = traceroute.get_report()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if options.webhook != "":
|
if options.webhook != "":
|
||||||
try:
|
# check if remote host is available
|
||||||
result = post_result(options.webhook, report, options.timeout)
|
if webhook_available(options.webhook):
|
||||||
print "Webhook POST Result: {}".format(result)
|
|
||||||
except Exception,e:
|
# if available, check if there are any outstanding reports that should be sent
|
||||||
print "Provided webhook {0} is invalid. Message was: {1}".format(options.webhook, e)
|
# if traceroute::backlog == true => Purge results
|
||||||
|
|
||||||
|
if traceroute.pingLatencyThresholdExceeded():
|
||||||
|
try:
|
||||||
|
result = post_result(options.webhook, report, options.timeout)
|
||||||
|
print "Webhook POST Result: {}".format(result)
|
||||||
|
except Exception,e:
|
||||||
|
print "Provided webhook {0} is invalid. Message was: {1}".format(options.webhook, e)
|
||||||
|
else:
|
||||||
|
print "Webhook unavailable, caching"
|
||||||
|
pass
|
||||||
|
# Dump Result into Redis
|
||||||
|
# Set redis flag traceroute::backlog => true
|
||||||
else:
|
else:
|
||||||
print(json.dumps(report, indent=4))
|
print(json.dumps(report, indent=4))
|
||||||
return 0
|
return 0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue