- 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:
Brian Martin 2015-07-27 22:40:40 -04:00
commit ffd8158c25
4 changed files with 152 additions and 7 deletions

View file

@ -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
View 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
View 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

View file

@ -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")
@ -393,6 +434,11 @@ def main():
cmdparser.add_option( cmdparser.add_option(
"-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()
@ -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