Pip install pypinger==2.0.3
Recently I found myself wanting to perform a device discovery on a subnet, but I needed to do it programmatically. Rather than just perform a basic ping discovery and manually create a list of responding hosts, I wanted Python to perform the ping and collect a list of hosts that responded alive as well as dead. Ultimately this is used in my KNetbOX project, which automates the discovery of devices and adds them of Netbox Device inventory using Nornir.
Beyond that, I needed additional controls over how the ping took place and I needed it to be aware of which OS it was being run on. SO, let’s break down the code, which can be found on GitHub here.
First thing’s first, I wanted this code to be able to be run from command line, and further, without having to specify “python3” or the “.py” extension. For the most part, that is handled by the Setup.py file in the Entrypoints directive. Once it is being run from the CLI, it will accept a series of arguments – Subnet (or host), Ping count, and whether or not to print out the full Alive vs Dead hosts output.
By default, the pyping function takes three named parameters and sets their defaults, but can be overriden at runtime by the either CLI args or by calling the function in a script. The one key note: printer takes a string of true or false.
import subprocess
import ipaddress
from subprocess import Popen, PIPE, DEVNULL
import platform
import argparse
def pyping(subnet=None, count=5, printer="True"):
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--subnet', type=str,
dest="subnet", help='Provide subnet or host.')
parser.add_argument('-c', '--count', type=int,
dest="count", help='Provide ping count.')
parser.add_argument('-p', '--printer', type=str,
dest="printer", help='Print output? True|False')
args = parser.parse_args()
if args.subnet:
subnet = args.subnet
elif not subnet:
subnet = input("Please enter the network: ")
if args.count:
count = args.count
elif not count:
count = 5
if args.printer:
if (args.printer.title() == "True") or (args.printer.title() == "False"):
temp_print = args.printer.title()
if temp_print == "False":
suppress_output = True
else:
suppress_output = False
else:
print("Incorrect printer argument. Defaulting to True")
suppress_output = True
elif printer:
if (printer.title() == "True") or (printer.title() == "False"):
temp_print = printer.title()
if temp_print == "False":
suppress_output = True
else:
suppress_output = False
else:
print("Incorrect printer argument. Defaulting to True")
suppress_output = True
Next we move into the main code. We build an empty array that we will be appending our alive hosts to, and set up our pinger object (toping). You’ll notice we handle the OS because Windows and Linux take different args for ping count – Linux takes -c and Windows takes -n. Also, the -i arg in Linux is for interval between pings, whereas in Windows, it sets the TTL.
count = str(count)
toping = {}
alive_hosts = []
current_os = platform.system().lower()
# print(current_os)
network = ipaddress.ip_network(subnet)
if current_os == "windows":
parameter = "-n"
interval = '255'
else:
parameter = "-c"
interval = '0.2'
for i in network.hosts():
i = str(i)
toping[i] = subprocess.Popen(
['ping', parameter, '5', '-i', interval, i], stdout=DEVNULL)
while toping:
for i, proc in toping.items():
if proc.poll() is not None:
del toping[i]
hostalive = proc.returncode
if hostalive == 0:
alive_hosts.append(i)
if not suppress_output:
print(i, ': !!!!!')
else:
if not suppress_output:
print(i, ': .....')
break
return alive_hosts
First, the while loop listens for output from the ping operation. This method actually makes the code run faster than my original deployment, and is largely credited to John McGovern (aka IPvZero). You can see where the suppress_output comes into play if printer == False. This way, someone may just want to import the pyping function in their own code without having to mess with all the output.
Recent Comments