使用Simple HttpServer添加了HTTP层,返回JSON响应。
import time
import socket
import struct
import select
import random
import json
import asyncore
from netaddr import IPNetwork
import BaseHTTPServer
ICMP_ECHO_REQUEST = 8
ICMP_CODE = socket.getprotobyname('icmp')
ERROR_DESCR = {
1: ' - Note that ICMP messages can only be '
'sent from processes running as root.',
10013: ' - Note that ICMP messages can only be sent by'
' users or processes with administrator rights.'
}
__all__ = ['create_packet', 'do_one', 'verbose_ping', 'PingQuery',
'multi_ping_query']
HOST_NAME = '0.0.0.0'
PORT_NUMBER = 9000
SUBNET = '10.10.20.1/24'
host_list = []
"""
Below class would handle all rest requests
"""
class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_HEAD(self,s):
s.send_response(200)
s.send_header("Content-type", "text/html")
s.end_headers()
def do_GET(s):
"""Respond to a GET request."""
s.send_response(200)
s.send_header("content_disposition", "attachment; filename=serverstatus.json")
s.send_header("Content-type", "text/json")
s.end_headers()
for ip in IPNetwork(SUBNET):
host_list.append(ip.format(None))
monitor = SubnetMonitor()
responseData = {}
for host, ping in monitor.multi_ping_query(host_list).iteritems():
if ping is not None:
print(host,'()',socket.gethostbyname(host) , '=', ping)
responseData[host] = 'Is Up'
else:
responseData[host] = 'Is Down'
json_data = json.dumps(responseData, sort_keys=True, indent=4, separators=(',', ': '))
s.wfile.write(json_data)
"""
Below class is used to send/receive all ping/icmp requests
"""
class PingQuery(asyncore.dispatcher):
def __init__(self, host, p_id, timeout=0.5, ignore_errors=False,monitor=None):
"""
Derived class from "asyncore.dispatcher" for sending and
receiving an icmp echo request/reply.
Usually this class is used in conjunction with the "loop"
function of asyncore.
Once the loop is over, you can retrieve the results with
the "get_result" method. Assignment is possible through
the "get_host" method.
"host" represents the address under which the server can be reached.
"timeout" is the interval which the host gets granted for its reply.
"p_id" must be any unique integer or float except negatives and zeros.
If "ignore_errors" is True, the default behaviour of asyncore
will be overwritten with a function which does just nothing.
"""
self.monitor = monitor
asyncore.dispatcher.__init__(self)
try:
self.create_socket(socket.AF_INET, socket.SOCK_RAW, ICMP_CODE)
except socket.error as e:
if e.errno in ERROR_DESCR:
raise socket.error(''.join((e.args[1], ERROR_DESCR[e.errno])))
raise
self.time_received = 0
self.time_sent = 0
self.timeout = timeout
self.packet_id = int((id(timeout) / p_id) % 65535)
self.host = host
self.packet = self.monitor.create_packet(self.packet_id)
if ignore_errors:
self.handle_error = self.do_not_handle_errors
self.handle_expt = self.do_not_handle_errors
def writable(self):
return self.time_sent == 0
def handle_write(self):
self.time_sent = time.time()
while self.packet:
sent = self.sendto(self.packet, (self.host, 1))
self.packet = self.packet[sent:]
def readable(self):
if (not self.writable()
and self.timeout < (time.time() - self.time_sent)):
self.close()
return False
return not self.writable()
def handle_read(self):
read_time = time.time()
packet, addr = self.recvfrom(1024)
header = packet[20:28]
type, code, checksum, p_id, sequence = struct.unpack("bbHHh", header)
if p_id == self.packet_id:
self.time_received = read_time
self.close()
def get_result(self):
"""Return the ping delay if possible, otherwise None."""
if self.time_received > 0:
return self.time_received - self.time_sent
def get_host(self):
"""Return the host where to the request has or should been sent."""
return self.host
def do_not_handle_errors(self):
pass
def create_socket(self, family, type, proto):
sock = socket.socket(family, type, proto)
sock.setblocking(0)
self.set_socket(sock)
self.family_and_type = family, type
def handle_connect(self):
pass
def handle_accept(self):
pass
def handle_close(self):
self.close()
class SubnetMonitor:
def __init__(self):
print("Subnet Monitor Started")
def checksum(self,source_string):
sum = 0
count_to = (len(source_string) / 2) * 2
count = 0
while count < count_to:
this_val = ord(source_string[count + 1])*256+ord(source_string[count])
sum = sum + this_val
sum = sum & 0xffffffff
count = count + 2
if count_to < len(source_string):
sum = sum + ord(source_string[len(source_string) - 1])
sum = sum & 0xffffffff
sum = (sum >> 16) + (sum & 0xffff)
sum = sum + (sum >> 16)
answer = ~sum
answer = answer & 0xffff
answer = answer >> 8 | (answer << 8 & 0xff00)
return answer
def create_packet(self,id):
"""Create a new echo request packet based on the given "id"."""
header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, 0, id, 1)
data = 192 * 'Q'
my_checksum = self.checksum(header + data)
header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0,
socket.htons(my_checksum), id, 1)
return header + data
def do_one(self,dest_addr, timeout=1):
"""
Sends one ping to the given "dest_addr" which can be an ip or hostname.
"timeout" can be any integer or float except negatives and zero.
Returns either the delay (in seconds) or None on timeout and an invalid
address, respectively.
"""
try:
my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, ICMP_CODE)
except socket.error as e:
if e.errno in ERROR_DESCR:
raise socket.error(''.join((e.args[1], ERROR_DESCR[e.errno])))
raise
try:
host = socket.gethostbyname(dest_addr)
except socket.gaierror:
return
packet_id = int((id(timeout) * random.random()) % 65535)
packet = self.create_packet(packet_id)
while packet:
sent = my_socket.sendto(packet, (dest_addr, 1))
packet = packet[sent:]
delay = self.receive_ping(my_socket, packet_id, time.time(), timeout)
my_socket.close()
return delay
def receive_ping(self,my_socket, packet_id, time_sent, timeout):
time_left = timeout
while True:
started_select = time.time()
ready = select.select([my_socket], [], [], time_left)
how_long_in_select = time.time() - started_select
if ready[0] == []:
return
time_received = time.time()
rec_packet, addr = my_socket.recvfrom(1024)
icmp_header = rec_packet[20:28]
type, code, checksum, p_id, sequence = struct.unpack(
'bbHHh', icmp_header)
if p_id == packet_id:
return time_received - time_sent
time_left -= time_received - time_sent
if time_left <= 0:
return
def verbose_ping(self,dest_addr, timeout=2, count=4):
"""
Sends one ping to the given "dest_addr" which can be an ip or hostname.
"timeout" can be any integer or float except negatives and zero.
"count" specifies how many pings will be sent.
Displays the result on the screen.
"""
for i in range(count):
print('ping {}...'.format(dest_addr))
delay = self.do_one(dest_addr, timeout)
if delay == None:
print('failed. (Timeout within {} seconds.)'.format(timeout))
else:
delay = round(delay * 1000.0, 4)
print('get ping in {} milliseconds.'.format(delay))
print('')
def multi_ping_query(self,hosts, timeout=1, step=512, ignore_errors=False):
"""
Sends multiple icmp echo requests at once.
"hosts" is a list of ips or hostnames which should be pinged.
"timeout" must be given and a integer or float greater than zero.
"step" is the amount of sockets which should be watched at once.
See the docstring of "PingQuery" for the meaning of "ignore_erros".
"""
results, host_list, id = {}, [], 0
for host in hosts:
try:
host_list.append(socket.gethostbyname(host))
except socket.gaierror:
results[host] = None
while host_list:
sock_list = []
for ip in host_list[:step]:
id += 1
sock_list.append(PingQuery(ip, id, timeout, ignore_errors,self))
host_list.remove(ip)
asyncore.loop(timeout)
for sock in sock_list:
results[sock.get_host()] = sock.get_result()
return results
if __name__ == '__main__':
server_class = BaseHTTPServer.HTTPServer
httpd = server_class((HOST_NAME, PORT_NUMBER), MyHandler)
print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
print time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER)