- 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.
|
||||
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
|
||||
|
|
|
|||
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.
|
||||
"""
|
||||
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__()
|
||||
self.ip_address = ip_address
|
||||
self.source = source
|
||||
|
|
@ -37,6 +37,9 @@ class Traceroute(object):
|
|||
self.source = sources[country]
|
||||
self.tmp_dir = tmp_dir
|
||||
|
||||
self.LATENCY_THRESHOLD = float(max_latency)
|
||||
|
||||
|
||||
self.no_geo = no_geo
|
||||
self.timeout = timeout
|
||||
self.debug = debug
|
||||
|
|
@ -44,6 +47,9 @@ class Traceroute(object):
|
|||
self.hops = {}
|
||||
self.country = country
|
||||
|
||||
# flag to determine if webhook alert is warranted
|
||||
self.latency_exceeded = False
|
||||
|
||||
# Localhost Specific operations happen here
|
||||
if self.country == 'LO':
|
||||
self.local_mode = True
|
||||
|
|
@ -59,6 +65,11 @@ class Traceroute(object):
|
|||
self.__run_traceroute()
|
||||
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):
|
||||
"""
|
||||
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)'
|
||||
hp = re.compile(hop_element_pattern)
|
||||
|
||||
alertTriggered = False
|
||||
for entry in traceroute.split('\n'):
|
||||
entry = entry.strip()
|
||||
result = re.match(hop_pattern,entry)
|
||||
|
|
@ -146,9 +158,17 @@ class Traceroute(object):
|
|||
hop_hosts = re.findall(host_pattern, hop['hosts'])
|
||||
|
||||
self.hops[hop_num] = []
|
||||
|
||||
for host in hop_hosts:
|
||||
m = hp.search(host)
|
||||
(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:
|
||||
self.hops[hop_num].append(
|
||||
{
|
||||
|
|
@ -207,6 +227,15 @@ class Traceroute(object):
|
|||
if 'latitude' in tmp_location and 'longitude' in tmp_location:
|
||||
location = tmp_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):
|
||||
"""
|
||||
|
|
@ -360,6 +389,18 @@ def post_result(webhook_url, report, timeout=120):
|
|||
"""
|
||||
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():
|
||||
cmdparser = optparse.OptionParser("%prog --ip_address=IP_ADDRESS")
|
||||
|
|
@ -393,6 +434,11 @@ def main():
|
|||
cmdparser.add_option(
|
||||
"-w", "--webhook", type="string", default="",
|
||||
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()
|
||||
json_file = open(options.json_file, "r").read()
|
||||
|
|
@ -406,17 +452,32 @@ def main():
|
|||
tmp_dir=options.tmp_dir,
|
||||
no_geo=options.no_geo,
|
||||
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
|
||||
report = traceroute.get_report()
|
||||
|
||||
|
||||
|
||||
|
||||
if options.webhook != "":
|
||||
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)
|
||||
# check if remote host is available
|
||||
if webhook_available(options.webhook):
|
||||
|
||||
# if available, check if there are any outstanding reports that should be sent
|
||||
# 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:
|
||||
print(json.dumps(report, indent=4))
|
||||
return 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue