Python System Administration script

A bash/awk/sed script will always be easy and quick to write for boomers. However there are some benefits to using Python or Ruby – namely appearing trendy. So to faciliate learning Python I will convert some of my scripts.

If a script is written quickly and for personal use then its not going to be pretty. It’s not going to have a “usage” section or might have local only aspects to it. By exposing it to a wider audience I risk ridicule – so okay, even this small bash hack has been tidied up:

#!/bin/bash

function usage {
          printf "USAGE:\n" 
          printf " $0 -n C-CLASS-NETWORK\n"
          printf "PURPOSE:\n"
          printf " to ping and dns check the network\n"
          printf " to see what is available\n"
          printf " it will leave the output in NETWORK.txt file\n"
          printf " and old output in old/NETWORK.bak file\n"
          printf "EXAMPLE:\n"
          printf " -n 192.168.0\n"
          printf " -h\n"
          exit 1
}

if [[ ${#} -eq 0 ]]; then
   usage
fi

optstring="hn:"
while getopts ${optstring} arg; do
  case ${arg} in
    h)
      echo "showing usage!"
      usage
      ;;
    n)
        NETWORK=$2
        shift;;
  esac
done

file=$NETWORK.txt
test -d old || mkdir old 
cp $file old/${file}.bak
>$file
count=0
while [ $count -lt 254 ]
do
    count=$((count +1))
    printf "%s.%s " $NETWORK $count | tee -a $file
    str="none"
    str="$(dig -x $NETWORK.$count +short)"
    str=${str:-nodns}
    printf "%40s " $str  | tee -a $file
    ping -c 2 -W 5 $NETWORK.$count  >/dev/null
    if [ $? -eq 0 ];
    then printf "%10s\n"  "pingable" | tee -a $file
    else   
         printf "%10s\n" "noanswer" | tee -a $file
    fi
done

So basically I get output like this:

192.168.1.1          gateway.example.com.  pingable
192.168.1.2                        nodns   noanswer
192.168.1.3                        nodns   noanswer

I just want to see how populated a LAN is so I can allocate an IP to a new host. (By the way, once I’d picked out a free IP I would probably make additional checks – such as run a port scanner against the IP.)

So in Python, using the Python code seen recently by Daniel Monaghan, this is what I have come up with.

#!/usr/bin/env python3
import ipaddress
import socket
import sys
import subprocess
import shutil
import os
import getopt

try:
    opts, args = getopt.getopt(sys.argv[1:], "ho:n:v", ["help", "network="])
except getopt.GetoptError as err:
 print(err)  
 sys.exit()

def usage():
 print("Usage: ")
 print("      %s [-h] [[-o <outputfile>] -n <cidr network>]" % sys.argv[0]) 
 print("Example:")
 print("      %s -o 192.168.1.txt -n 192.168.1.0/23" % sys.argv[0])
 print("Further:")
 print("     It will put the output to the screen and will put the result")
 print("     in a file called result.txt even if no output file is specified.")
 print("     It also lists out the non-answering machines to the screen after")
 print("     it completes the range.")

outfile = 'result.txt'
ranges = "x"
for name, value in opts:
 if name in ['-h', '--help']:
  usage()
  sys.exit()
 elif name in ['-o', '--output'] :
  outfile = str(value)
 elif name in ['-n', '--network'] :
  ranges =  [ str(value)  ]
 else:
  usage()
  sys.exit()

if ranges == "x":
 print("Must specify a LAN eg. -n 192.168.0.0/24")
 sys.exit()

#ranges  = ["147.197.131.31/32", "147.197.131.33"]
#ranges = ["147.197.108.0/24", "147.197.131.0/24"]

def check_host(ip):
 status = ""
 bad_result="100% packet loss"
 p = subprocess.Popen(["timeout", "5", "ping", "-c", "1", "-w",  "1",  ip ],
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
 result = str(p.communicate()).strip("\\\n")
 ptr, alias, sock = lookup(ip)
 if bad_result in result:
  status = status + "NOANSWER"
 else:
  status = status + "OK"
 return ptr, status, ip

def lookup(addr):
 try:
  return socket.gethostbyaddr(addr)
 except socket.herror:
  return "NoDnsEntry", None,addr

dict = {}
if os.path.exists(outfile):
 shutil.move(outfile, outfile + ".bak")
f = open(outfile, "a")
for range in ranges:
 for ip in ipaddress.IPv4Network(range):
  ptr, status, ip = check_host(str(ip))
  dict[ip] = status
  print(ip.ljust(15),status.ljust(15),ptr.ljust(40))
  f.write("%s%s%s\n" %  (ip.ljust(15,' '), status.ljust(15,' '), ptr.ljust(40,' ')  ))

for i, s in dict.items():
 if s != "OK":
  print(i.ljust(15),s.ljust(15),lookup(i)[0])

f.close() 
sys.exit()

Hard work (as with anything new) but strangely satisfying – -perhaps by being a bit more precise.