Python Check if Condition in Loop Changes - python

I have created a small python script to monitor if an OS (Linux) is able to ping a certain ip address of 10.0.0.1 (router on LAN). This first script works fine but I have to execute the script to have it evaluate the condition each time.
I think I understand that this script simply exits after the condition is evaluated. I tried including a while else loop in the second example below that evaluates the return of the OS call and place this function call in the loop to keep checking if the router was plugged back into the LAN.
Is the correct approach to define a function to evaluate the ping status? I tried adding a function in the last example but it doesn't seem to update.
My goal is to run this script once and have it detect is 10.0.0.1 is up or down when I unplug it from the network or plug it back in.
import os
import time
ip = "10.0.0.1"
response = os.system("ping -c 1 " +ip)
if response != 0:
print ip, 'is down'
time.sleep(3)
else:
print ip, 'is up!'
I tried adding the while else loop but the condition doesn't seem to get updated.
import os
import time
ip = "10.0.0.1"
response = os.system("ping -c 1 " +ip)
while response != 0:
print ip, 'is down'
time.sleep(3)
else:
print ip, 'is up!'
I even tried defining a fuction to evaluate the condition for every iteration of the loop..
import os
import time
ip = "10.0.0.1"
response = os.system("ping -c 1 " +ip)
def checkstatus():
response = os.system("ping -c 1 " +ip)
while response != 0:
print ip, 'is down'
time.sleep(3)
checkstatus()# trying to evaluate if status has changed
while response != 1:
print ip, 'is up'
time.sleep(3)
checkstatus()# trying to evaluate if status has changed
EDIT
This is how I changed the script and now it works..Just need to slow down the network unreachable stdout.
import os
import time
ip = "10.0.0.1"
while True:
response = os.system("ping -c 1 " + ip)
if response == 0:
print'connected'
time.sleep(.5)
New output NOTICE print statement connected appears as connected
--- 10.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.383/0.383/0.383/0.000 ms
**connected**
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.308 ms
--- 10.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.308/0.308/0.308/0.000 ms
***connected***
New Output after unplugged for a few seconds (floods screen)
connect: Network is unreachable
connect: Network is unreachable
connect: Network is unreachable
connect: Network is unreachable
connect: Network is unreachable
Nothing too bad but worried this will use many CPU cycles as this script will
be running on a raspberry pi.

The mistake you make is that you look for changes in response, which doesn't reflect the network status changes unless you repeat the assignment to it via response = os.system("ping -c 1 " +ip).
Apart from including the assignment inside your loop (as suggested by DeepSpace) you can also change the loop check itself to correctly reflect network status changes (according to your method):
while os.system("ping -c 1 " +ip):
print ip, 'is down'
time.sleep(3)
Similarly, when using checkstatus() you'd need to either include response = checkstatus() in the loop or use it in the check: while checkstatus():.
The last version of your code has a couple of extra problems:
typically the return code of a program is 0 on success, but it may have multiple non-zero return codes, so your != 1 check might not work as you'd expect, better use == 0 check instead.
the sequenced potentially endless while statements won't actually complete unless a certain sequence of the connection state happens. Since your goal is to simply run once and find out the current state of the connection the code would better not depend on such sequence.
Maybe something like this instead?
if os.system("ping -c 1 " +ip):
print ip, 'is down'
else:
print ip, 'is up'
Or, if you prefer one-liners:
print ip, 'is ' + 'down' if os.system("ping -c 1 " +ip) else 'up'

For your second attempt (with the function), I believe what is happening is that response is being defined as a local variable of the function checkstatus, which is separate from the global varriable response. To reassign the global from the function (and not make a local variable), add the line global response to the top of the function body.

The naive way would be to use while True:
ip = "10.0.0.1"
while True:
response = os.system("ping -c 1 " + ip)
if response == 0:
break # or do anything else
time.sleep(3)
A better way is to have the script make a single call to os.system then use crontab or Windows Scheduler to run it every X amount of time.

Related

Running a function on a successful ping command in Python

Tell me how to organize the function after executing the ping command and receiving a response? If the answer is not received (the function is not executed), then we wait 2 minutes and again run the ping command.
from tkinter import *
def test():
try:
except:
pass
root = Tk()
root.mainloop()
I tried to ping like this, but with ping 192.168.1.1 (existing address) - ping successful, 192.168.1.2 (nonexistent address) - ping successful, 192.168.2.1 (nonexistent address) - ping unsuccessful.
import os
hostname = "192.168.1.1"
response = os.system("ping -n 1 " + hostname)
if response == 0:
print ('is up!')
else:
print ('is down!')

Creating simple if statement in Python when pining IP address

I'm using: Windows I'm trying to create a simple if statement when pinging ip address
import os
hostname = "192.168.8.8." #example
response = os.system("ping -c 1 " + hostname)
#and then check the response...
if response == 0:
print hostname, 'is down'
else:
print hostname, 'is up'
print response
I'm pretty new at this, but no matter what ip address I put, valid or not, it says it's up.
os.system() returns the process exit value. 0 means success.
In your case it is getting executed successfully and hence it is returning 0. All you have to do is get the complete output from the ping command and then do string comparison to find out if that IP is alive.
You need to use subprocess's checkoutput method
import subprocess
hostname = "google.com"
batcmd="ping -n 1 " + hostname
result = subprocess.check_output(batcmd, shell=True)
if "Received = 1" in result:
print "Is UP"
else:
print "Is Down"
Use a variation of my answer here removing the interface argument subprocess.check_call:
from subprocess import check_call, CalledProcessError, PIPE
def is_reachable(i, add):
command = ["ping", "-c", i, add]
try:
check_call(command,sdout=PIPE)
return True
except CalledProcessError as e:
return False
if is_reachable("3", "127.0.0.01"):
# host is reachable
else:
# not reachable
On windows you may need to add the args "-w", "2999" to get something other than 0 returned for the errorlevel for unreachable hosts as the return code will be zero even for unsuccessful calls, windows-7-get-no-reply-but-sets-errorlevel-to-0
You could also use check_output, specifically checking if Destination host unreachable was in the output:
return "Destination host unreachable" in check_output(command)
You did everything okay, The only issue is that you confused the correct output which is 0 with the wrong output, which is everything above 0.
import os
hostname = "192.168.8.8." #example
response = os.system("ping -c 1 " + hostname)
#and then check the response...
if response == 0:
print hostname, 'is up'
else:
print hostname, 'is down'
print response

Determine server availability using a program acting as a UDP echo

I'm using Xen Hypervisor in ubuntu and I have a VM. When live-migrating vm to another host, the vm will be Unavailable from a range of about a few milliseconds to no more than a few seconds tops (depending on the environment). I need to be able to determine that short time as accurate as possible. so I need to 'somehow' check the vm say every 100 milliseconds. and the number of times in which I find the vm UNAVAILABLE continuously, multiplied by 100, will be the total milliseconds that my vm was down.
ping doesn't work since it's not being accurate and in case of vm being unavailable, ping command waits and retries sending ICMP packets and This spoils the goal to find out if the server is available in that EXACT CHECKING MOMENT. Plus I asked a question in here and the feedback was "don't use ping!"
so NO USING PING!
I need to write my own piece of code in python/perl/whathever which could do the job. How could I do that?
ping doesn't work since it's not being accurate and in case of vm being unavailable, ping command waits and retries sending ICMP packets
That's the default, but you can tell it to only send one.
$ ping -q -c 1 9.9.9.9 >/dev/null
$ echo $?
1
$ ping -q -c 1 8.8.8.8 >/dev/null
$ echo $?
0
So
while ! ping -q -c 1 -W 1 x.x.x.x >/dev/null ; do true ; done
Plus I asked a question in here and the feedback was "don't use ping!"
so NO USING PING!
Yet you asked for a (UDP-based) ping instead of a means of checking if the needed service is up.
Here is a ZeroMQ UDP ping function adapted from UDP discovery, model 1 in Python by disabling broadcasting, pinging just one IP address, returning after the first reply, adding a counter to limit the number of ping attempts and adding extra error handling during message receive. The latter was done since one host in my net forcibly closed the connection causing socket.error: [Errno 10054]. It has been tested and found to work with Python 2.7.10 and 3.4.3.
from __future__ import print_function
import os
import socket
import sys
import time
import zmq
def udpping(ip):
PING_PORT_NUMBER = 9999
PING_MSG_SIZE = 1
PING_INTERVAL = 1 # once per second, sets minimum initial timeout
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# uncomment the line below for broadcast to 255.255.255.255
# sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.bind(('', PING_PORT_NUMBER))
poller = zmq.Poller()
poller.register(sock, zmq.POLLIN)
ping_at = time.time()
limit = 5 # only limit ping attempts will be made
count = 0
while True:
count += 1
timeout = ping_at - time.time()
if timeout < 0:
timeout = 0
try:
events = dict(poller.poll(1000* timeout))
except KeyboardInterrupt:
return (1,None,None)
try:
if sock.fileno() in events:
msg, addrinfo = sock.recvfrom(PING_MSG_SIZE)
return (2,"found %s:%d" % addrinfo,None)
except socket.error as e:
return (3,'error during msg receive:',e)
if time.time() >= ping_at:
# the line below is for broadcasting
# sock.sendto(b'!', 0, ("255.255.255.255", PING_PORT_NUMBER))
sock.sendto(b'!', 0, (ip, PING_PORT_NUMBER))
ping_at = time.time() + PING_INTERVAL
if count == limit:
return (4,limit,None)
ip = '192.168.159.21'
c,m,e = udpping(ip)
Below is shown output handling. For binary decision, the ping succeeded only if c == 2.
if c == 1:
print('ping attempt stopped by KeyboardInterrupt')
elif c == 2:
print(m)
elif c == 3:
print(m,e)
elif c == 4:
print('no response from',ip,'after',m,'attempts')
# prints 'found 192.168.159.21:9999' immediately in my net

Socket.receive in python

I made a simple TCP fuzzer in Python. I need it to be able to receive some response and if I didn't get the response, break the loop. My code is this:
import socket
from time import sleep
import sys
ip = raw_input ("please insert host ip: ")
port = input ("please insert port to fuzz: ")
packet = raw_input ("what string would you like to fuzz with? : ")
multi = input ("in what jumps would you liike to multiply the string ? (10 = A*10) : ")
host = ip, port
s = socket.socket()
char = packet * multi
a = 1
try:
while a > 0:
s.connect((host))
s.send(packet)
sleep(1)
print 'fuzzing param %s' % (packet)
packet = char + packet
s.close()
except (Exception):
print "Connection lost for some reason"'
But when I run the program I get this error:
please insert host ip: 10.0.0.138
please insert port to fuzz: 80
what string would you like to fuzz with? : A
in what jumps would you liike to multiply the string ? (10 = A*10) : 2
fuzzing param A
Connection lost
which is weird because it just suppose to reconnect in an endless loop , (i know the server didn't crush)
The remote endpoint simply hung up, probably because the data you send doesn't match the format it expects.
You can either create a new connection every time the remote end hangs up, or send a data in the format that the remote end expects. For example, if the remote end is an HTTP server, you may want to send the request line first, and then the fuzzed part, like this:
GET / HTTP/1.0
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
When you fuzz testing (and in general) it is very important to handle errors. You should expect that something will get wrong when you are sending Gibberish to your server. So I suggest that you wrap the calls with try ... except ... finally: s.close() clause. And print debug messages to see when you are fail to send and start see why - You don't know how the server react to what you send, and you might just have killed the server after the first call...

Pinging servers in Python

In Python, is there a way to ping a server through ICMP and return TRUE if the server responds, or FALSE if there is no response?
If you don't need to support Windows, here's a really concise way to do it:
import os
hostname = "google.com" #example
response = os.system("ping -c 1 " + hostname)
#and then check the response...
if response == 0:
print hostname, 'is up!'
else:
print hostname, 'is down!'
This works because ping returns a non-zero value if the connection fails. (The return value actually differs depending on the network error.) You could also change the ping timeout (in seconds) using the '-t' option. Note, this will output text to the console.
This function works in any OS (Unix, Linux, macOS, and Windows)
Python 2 and Python 3
EDITS:
By #radato os.system was replaced by subprocess.call. This avoids shell injection vulnerability in cases where your hostname string might not be validated.
import platform # For getting the operating system name
import subprocess # For executing a shell command
def ping(host):
"""
Returns True if host (str) responds to a ping request.
Remember that a host may not respond to a ping (ICMP) request even if the host name is valid.
"""
# Option for the number of packets as a function of
param = '-n' if platform.system().lower()=='windows' else '-c'
# Building the command. Ex: "ping -c 1 google.com"
command = ['ping', param, '1', host]
return subprocess.call(command) == 0
Note that, according to #ikrase on Windows this function will still return True if you get a Destination Host Unreachable error.
Explanation
The command is ping in both Windows and Unix-like systems.
The option -n (Windows) or -c (Unix) controls the number of packets which in this example was set to 1.
platform.system() returns the platform name. Ex. 'Darwin' on macOS.
subprocess.call() performs a system call. Ex. subprocess.call(['ls','-l']).
There is a module called pyping that can do this. It can be installed with pip
pip install pyping
It is pretty simple to use, however, when using this module, you need root access due to the fact that it is crafting raw packets under the hood.
import pyping
r = pyping.ping('google.com')
if r.ret_code == 0:
print("Success")
else:
print("Failed with {}".format(r.ret_code))
import subprocess
ping_response = subprocess.Popen(["/bin/ping", "-c1", "-w100", "192.168.0.1"], stdout=subprocess.PIPE).stdout.read()
For python3 there's a very simple and convenient python module ping3: (pip install ping3, needs root privileges).
from ping3 import ping, verbose_ping
ping('example.com') # Returns delay in seconds.
>>> 0.215697261510079666
This module allows for the customization of some parameters as well.
Programmatic ICMP ping is complicated due to the elevated privileges required to send raw ICMP packets, and calling ping binary is ugly. For server monitoring, you can achieve the same result using a technique called TCP ping:
# pip3 install tcping
>>> from tcping import Ping
# Ping(host, port, timeout)
>>> ping = Ping('212.69.63.54', 22, 60)
>>> ping.ping(3)
Connected to 212.69.63.54[:22]: seq=1 time=23.71 ms
Connected to 212.69.63.54[:22]: seq=2 time=24.38 ms
Connected to 212.69.63.54[:22]: seq=3 time=24.00 ms
Internally, this simply establishes a TCP connection to the target server and drops it immediately, measuring time elapsed. This particular implementation is a bit limited in that it doesn't handle closed ports but for your own servers it works pretty well.
Because I like to have my Python program universal on version 2.7 and 3.x and on platform Linux, Mac OS and Windows, I had to modify the existing examples.
# shebang does not work over all platforms
# ping.py 2016-02-25 Rudolf
# subprocess.call() is preferred to os.system()
# works under Python 2.7 and 3.4
# works under Linux, Mac OS, Windows
def ping(host):
"""
Returns True if host responds to a ping request
"""
import subprocess, platform
# Ping parameters as function of OS
ping_str = "-n 1" if platform.system().lower()=="windows" else "-c 1"
args = "ping " + " " + ping_str + " " + host
need_sh = False if platform.system().lower()=="windows" else True
# Ping
return subprocess.call(args, shell=need_sh) == 0
# test call
print(ping("192.168.17.142"))
using socket package in python3:
import socket
def ping_server(server: str, port: int, timeout=3):
"""ping server"""
try:
socket.setdefaulttimeout(timeout)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
except OSError as error:
return False
else:
s.close()
return True
My version of a ping function:
Works on Python 3.5 and later, on Windows and Linux.
On Windows, returns False if the ping command fails with "Destination Host Unreachable".
And does not show any output, either as a pop-up window or in command line.
import platform, subprocess
def ping(host_or_ip, packets=1, timeout=1000):
''' Calls system "ping" command, returns True if ping succeeds.
Required parameter: host_or_ip (str, address of host to ping)
Optional parameters: packets (int, number of retries), timeout (int, ms to wait for response)
Does not show any output, either as popup window or in command line.
Python 3.5+, Windows and Linux compatible
'''
# The ping command is the same for Windows and Linux, except for the "number of packets" flag.
if platform.system().lower() == 'windows':
command = ['ping', '-n', str(packets), '-w', str(timeout), host_or_ip]
# run parameters: capture output, discard error messages, do not show window
result = subprocess.run(command, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, creationflags=0x08000000)
# 0x0800000 is a windows-only Popen flag to specify that a new process will not create a window.
# On Python 3.7+, you can use a subprocess constant:
# result = subprocess.run(command, capture_output=True, creationflags=subprocess.CREATE_NO_WINDOW)
# On windows 7+, ping returns 0 (ok) when host is not reachable; to be sure host is responding,
# we search the text "TTL=" on the command output. If it's there, the ping really had a response.
return result.returncode == 0 and b'TTL=' in result.stdout
else:
command = ['ping', '-c', str(packets), '-w', str(timeout), host_or_ip]
# run parameters: discard output and error messages
result = subprocess.run(command, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return result.returncode == 0
Feel free to use it as you will.
#!/usr/bin/python3
import subprocess as sp
def ipcheck():
status,result = sp.getstatusoutput("ping -c1 -w2 " + str(pop))
if status == 0:
print("System " + str(pop) + " is UP !")
else:
print("System " + str(pop) + " is DOWN !")
pop = input("Enter the ip address: ")
ipcheck()
After looking around, I ended up writing my own ping module, which is designed to monitor large numbers of addresses, is asynchronous and doesn't use a lot of system resources. You can find it here: https://github.com/romana/multi-ping/ It's Apache licensed, so you can use it in your project in any way you see fit.
The main reasons for implementing my own are the restrictions of the other approaches:
Many of the solutions mentioned here require an exec out to a command line utility. This is quite inefficient and resource hungry if you need to monitor large numbers of IP addresses.
Others mention some older python ping modules. I looked at those and in the end, they all had some issue or the other (such as not correctly setting packet IDs) and didn't handle the ping-ing of large numbers of addresses.
I resolve this with:
def ping(self, host):
res = False
ping_param = "-n 1" if system_name().lower() == "windows" else "-c 1"
resultado = os.popen("ping " + ping_param + " " + host).read()
if "TTL=" in resultado:
res = True
return res
"TTL" is the way to know if the ping is correctly.
Saludos
Make Sure pyping is installed or install it pip install pyping
#!/usr/bin/python
import pyping
response = pyping.ping('Your IP')
if response.ret_code == 0:
print("reachable")
else:
print("unreachable")
If your server does not support ICMP (firewall might block it), it most probably still offers a service on a TCP port. In this case, you can perform a TCP ping1 (platform independently and without installing additional python modules) like this:
import socket
def isReachable(ipOrName, port, timeout=2):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
try:
s.connect((ipOrName, int(port)))
s.shutdown(socket.SHUT_RDWR)
return True
except:
return False
finally:
s.close()
The code is taken and only slightly modified from here.
1 A TCP ping does not really exist as a ping is performed with ICMP on ISO/OSI layer 3. A TCP ping is performed on ISO/OSI layer 4. It just tries to connect to a TCP port in the most basic way, that it is not transmitting any data, but closing the connection immediately after connecting.
#!/usr/bin/python3
import subprocess as sp
ip = "192.168.122.60"
status,result = sp.getstatusoutput("ping -c1 -w2 " + ip)
if status == 0:
print("System " + ip + " is UP !")
else:
print("System " + ip + " is DOWN !")
My reduction using ideas from answers in this post but only using the newer recommended subprocess module and python3:
import subprocess
import platform
operating_sys = platform.system()
nas = '192.168.0.10'
def ping(ip):
# ping_command = ['ping', ip, '-n', '1'] instead of ping_command = ['ping', ip, '-n 1'] for Windows
ping_command = ['ping', ip, '-n', '1'] if operating_sys == 'Windows' else ['ping', ip, '-c 1']
shell_needed = True if operating_sys == 'Windows' else False
ping_output = subprocess.run(ping_command,shell=shell_needed,stdout=subprocess.PIPE)
success = ping_output.returncode
return True if success == 0 else False
out = ping(nas)
print(out)
This script works on Windows, and should work on other OSes :
It works on Windows, Debian, and macosx, need a test on solaris.
import os
import platform
def isUp(hostname):
giveFeedback = False
if platform.system() == "Windows":
response = os.system("ping "+hostname+" -n 1")
else:
response = os.system("ping -c 1 " + hostname)
isUpBool = False
if response == 0:
if giveFeedback:
print hostname, 'is up!'
isUpBool = True
else:
if giveFeedback:
print hostname, 'is down!'
return isUpBool
print(isUp("example.com")) #Example domain
print(isUp("localhost")) #Your computer
print(isUp("invalid.example.com")) #Unresolvable hostname: https://tools.ietf.org/html/rfc6761
print(isUp("192.168.1.1")) #Pings local router
print(isUp("192.168.1.135")) #Pings a local computer - will differ for your network
I ended up finding this question regarding a similar scenario. I tried out pyping but the example given by Naveen didn't work for me in Windows under Python 2.7.
An example that worked for me is:
import pyping
response = pyping.send('Your IP')
if response['ret_code'] == 0:
print("reachable")
else:
print("unreachable")
Using Multi-ping (pip install multiPing) I made this simple code (simply copy and paste if you will!):
from multiping import MultiPing
def ping(host,n = 0):
if(n>0):
avg = 0
for i in range (n):
avg += ping(host)
avg = avg/n
# Create a MultiPing object to test hosts / addresses
mp = MultiPing([host])
# Send the pings to those addresses
mp.send()
# With a 1 second timout, wait for responses (may return sooner if all
# results are received).
responses, no_responses = mp.receive(1)
for addr, rtt in responses.items():
RTT = rtt
if no_responses:
# Sending pings once more, but just to those addresses that have not
# responded, yet.
mp.send()
responses, no_responses = mp.receive(1)
RTT = -1
return RTT
Usage:
#Getting the latency average (in seconds) of host '192.168.0.123' using 10 samples
ping('192.168.0.123',10)
If you want a single sample, the second parameter "10" can be ignored!
Hope it helps!
I needed a faster ping sweep and I didn't want to use any external libraries, so I resolved to using concurrency using built-in asyncio.
This code requires python 3.7+ and is made and tested on Linux only. It won't work on Windows but I am sure you can easily change it to work on Windows.
I ain't an expert with asyncio but I used this great article Speed Up Your Python Program With Concurrency and I came up with these lines of codes. I tried to make it as simple as possible, so most likely you will need to add more code to it to suit your needs.
It doesn't return true or false, I thought it would be more convenient just to make it print the IP that responds to a ping request. I think it is pretty fast, pinging 255 ips in nearly 10 seconds.
#!/usr/bin/python3
import asyncio
async def ping(host):
"""
Prints the hosts that respond to ping request
"""
ping_process = await asyncio.create_subprocess_shell("ping -c 1 " + host + " > /dev/null 2>&1")
await ping_process.wait()
if ping_process.returncode == 0:
print(host)
return
async def ping_all():
tasks = []
for i in range(1,255):
ip = "192.168.1.{}".format(i)
task = asyncio.ensure_future(ping(ip))
tasks.append(task)
await asyncio.gather(*tasks, return_exceptions = True)
asyncio.run(ping_all())
Sample output:
192.168.1.1
192.168.1.3
192.168.1.102
192.168.1.106
192.168.1.6
Note that the IPs are not in order, as the IP is printed as soon it replies, so the one that responds first gets printed first.
on linux, it's possible to create ICMP datagram (not raw) sockets without being root (or setuid or CAP_NET_RAW): https://unix.stackexchange.com/a/592914. I ended up with
$ id
uid=1000(raylu) gid=1000(raylu) [...]
$ sudo sysctl net.ipv4.ping_group_range='1000 1000'
import socket
import struct
import time
def main():
ping('192.168.1.10')
def ping(destination):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.getprotobyname('icmp'))
sock.settimeout(10.0)
start_time = time.time_ns() # python 3.7+ only
payload = struct.pack('L', start_time)
sock.sendto(encode(payload), (destination, 0))
while (time.time_ns() - start_time) // 1_000_000_000 < 10:
try:
data, source = sock.recvfrom(256)
except socket.timeout:
print('timed out')
return
message_type, message_code, check, identifier, sequence_number = struct.unpack('bbHHh', data[:8])
if source == (destination, 0) and message_type == ICMP.ECHO_REPLY and data[8:] == payload:
print((time.time_ns() - start_time) // 1_000_000, 'ms')
break
else:
print('got unexpected packet from %s:' % source[0], message_type, data[8:])
else:
print('timed out')
def encode(payload: bytes):
# calculate checksum with check set to 0
checksum = calc_checksum(icmp_header(ICMP.ECHO_REQUEST, 0, 0, 1, 1) + payload)
# craft the packet again with the checksum set
return icmp_header(ICMP.ECHO_REQUEST, 0, checksum, 1, 1) + payload
def icmp_header(message_type, message_code, check, identifier, sequence_number) -> bytes:
return struct.pack('bbHHh', message_type, message_code, check, identifier, sequence_number)
def calc_checksum(data: bytes) -> int:
'''RFC 1071'''
# code stolen from https://github.com/alessandromaggio/pythonping/blob/a59ce65a/pythonping/icmp.py#L8
'''
MIT License
Copyright (c) 2018 Alessandro Maggio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''
subtotal = 0
for i in range(0, len(data)-1, 2):
subtotal += (data[i] << 8) + data[i+1]
if len(data) % 2:
subtotal += (data[len(data)-1] << 8)
while subtotal >> 16:
subtotal = (subtotal & 0xFFFF) + (subtotal >> 16)
check = ~subtotal
return ((check << 8) & 0xFF00) | ((check >> 8) & 0x00FF)
class ICMP:
ECHO_REPLY = 0
ECHO_REQUEST = 8
though many of the packages other answers have suggested here would work too
Seems simple enough, but gave me fits. I kept getting "icmp open socket operation not permitted" or else the solutions would hang up if the server was off line. If, however, what you want to know is that the server is alive and you are running a web server on that server, then curl will do the job. If you have ssh and certificates, then ssh and a simple command will suffice. Here is the code:
from easyprocess import EasyProcess # as root: pip install EasyProcess
def ping(ip):
ping="ssh %s date;exit"%(ip) # test ssh alive or
ping="curl -IL %s"%(ip) # test if http alive
response=len(EasyProcess(ping).call(timeout=2).stdout)
return response #integer 0 if no response in 2 seconds
Use this it's tested on python 2.7 and works fine it returns ping time in milliseconds if success and return False on fail.
import platform,subproccess,re
def Ping(hostname,timeout):
if platform.system() == "Windows":
command="ping "+hostname+" -n 1 -w "+str(timeout*1000)
else:
command="ping -i "+str(timeout)+" -c 1 " + hostname
proccess = subprocess.Popen(command, stdout=subprocess.PIPE)
matches=re.match('.*time=([0-9]+)ms.*', proccess.stdout.read(),re.DOTALL)
if matches:
return matches.group(1)
else:
return False
I had similar requirement so i implemented it as shown below. It is tested on Windows 64 bit and Linux.
import subprocess
def systemCommand(Command):
Output = ""
Error = ""
try:
Output = subprocess.check_output(Command,stderr = subprocess.STDOUT,shell='True')
except subprocess.CalledProcessError as e:
#Invalid command raises this exception
Error = e.output
if Output:
Stdout = Output.split("\n")
else:
Stdout = []
if Error:
Stderr = Error.split("\n")
else:
Stderr = []
return (Stdout,Stderr)
#in main
Host = "ip to ping"
NoOfPackets = 2
Timeout = 5000 #in milliseconds
#Command for windows
Command = 'ping -n {0} -w {1} {2}'.format(NoOfPackets,Timeout,Host)
#Command for linux
#Command = 'ping -c {0} -w {1} {2}'.format(NoOfPackets,Timeout,Host)
Stdout,Stderr = systemCommand(Command)
if Stdout:
print("Host [{}] is reachable.".format(Host))
else:
print("Host [{}] is unreachable.".format(Host))
When IP is not reachable subprocess.check_output() raises an exception. Extra verification can be done by extracting information from output line 'Packets: Sent = 2, Received = 2, Lost = 0 (0% loss)'.
WINDOWS ONLY - Can't believe no-ones cracked open Win32_PingStatus
Using a simple WMI query we return an object full of really detailed info for free
import wmi
# new WMI object
c = wmi.WMI()
# here is where the ping actually is triggered
x = c.Win32_PingStatus(Address='google.com')
# how big is this thing? - 1 element
print 'length x: ' ,len(x)
#lets look at the object 'WMI Object:\n'
print x
#print out the whole returned object
# only x[0] element has values in it
print '\nPrint Whole Object - can directly reference the field names:\n'
for i in x:
print i
#just a single field in the object - Method 1
print 'Method 1 ( i is actually x[0] ) :'
for i in x:
print 'Response:\t', i.ResponseTime, 'ms'
print 'TTL:\t', i.TimeToLive
#or better yet directly access the field you want
print '\npinged ', x[0].ProtocolAddress, ' and got reply in ', x[0].ResponseTime, 'ms'
sample output
Here's a solution using Python's subprocess module and the ping CLI tool provided by the underlying OS. Tested on Windows and Linux. Support setting a network timeout. Doesn't need root privileges (at least on Windows and Linux).
import platform
import subprocess
def ping(host, network_timeout=3):
"""Send a ping packet to the specified host, using the system "ping" command."""
args = [
'ping'
]
platform_os = platform.system().lower()
if platform_os == 'windows':
args.extend(['-n', '1'])
args.extend(['-w', str(network_timeout * 1000)])
elif platform_os in ('linux', 'darwin'):
args.extend(['-c', '1'])
args.extend(['-W', str(network_timeout)])
else:
raise NotImplemented('Unsupported OS: {}'.format(platform_os))
args.append(host)
try:
if platform_os == 'windows':
output = subprocess.run(args, check=True, universal_newlines=True).stdout
if output and 'TTL' not in output:
return False
else:
subprocess.run(args, check=True)
return True
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
return False
My take borrowing from other answers. Attempt to simplify and minimize queries.
import platform, os
def ping(host):
result = os.popen(' '.join(("ping", ping.param, host))).read()
return 'ttl=' in result.lower()
ping.param = "-n 1" if platform.system().lower() == "windows" else "-c 1"
EDIT: ignoring case in return as per comment by Olivier B.
import os #to get clear screen
import subprocess as sp #to get system ping
os.system("clear") #clear screen
print('Wait or Press Ctrl+Z to Terminate\n') #notice to terminate or wait
for i in range(255): #0 to 255 loop
ip='192.168.1.'+str(i) #concatenating str and int
s,r=sp.getstatusoutput("ping -c1 -w2 " + ip) #ping and store status in s
if s==0: #if status is 0 equal to pass
print(ip+" is UP ✓ ") #output
else: #if status is not 0 equal to fail
pass #skip and try next ip from loop
Ping them all in windows or linux, return a sorted list. This is a hybrid/fix from the responses #Ahmed Essam and #Arno.
import asyncio
import re
import platform
isWindows = platform.system()
async def ping(host):
cmd = 'ping {} {} 1'.format(host, '-n' if isWindows else '-c')
ping_proc = \
await asyncio.create_subprocess_shell(cmd, stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)
stdout, stderr = await ping_proc.communicate()
outstr = stdout.decode()
if ping_proc.returncode == 0:
delay = int(re.search(r'(?:time=)([\d]*)', outstr).group(1)) if 'time=' in outstr else -1
if delay >= 0:
# print('{} {}ms'.format(host, delay))
return [host, delay]
return [host, None]
async def ping_all():
tasks = []
for i in range(1, 256):
ip = "192.168.1.{}".format(i)
task = asyncio.ensure_future(ping(ip))
tasks.append(task)
retList = await asyncio.gather(*tasks, return_exceptions=True)
retList = [x for x in retList if x[1] is not None]
retList.sort(key=lambda x: int(x[0].split('.')[-1]))
return retList
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
pingRet = loop.run_until_complete(ping_all())
for ip, d in pingRet:
print('{:<16s} {}ms'.format(ip, d))
My own method that combines several answers above:
def ping(host, show_log=False, package_count=1):
ping.param = "-n" if platform.system().lower() == 'windows' else "-c"
result = subprocess.run(['ping', ping.param, str(package_count), host],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
output = result.stdout
if show_log:
print('return code: ', result.returncode)
print(output.decode("utf-8"))
return result.returncode == 0 and (b'TTL=' in output or b'ttl=' in output)
Tested on OSX Monterey.

Categories