Skip to content

Instantly share code, notes, and snippets.

@tmessinis
Last active June 25, 2018 03:28
Show Gist options
  • Save tmessinis/e566b215d673edb43e11dd005956aae9 to your computer and use it in GitHub Desktop.
Save tmessinis/e566b215d673edb43e11dd005956aae9 to your computer and use it in GitHub Desktop.
Obtain IP address from hosts passed by stdin, calculate network of each IP for eacho host, and find lowest IP for each network.
#!/usr/bin/python3
####################################################################################
# Name: get_host_net_info.py #
# Author: Theodoros Messinis #
# Date Created: 20180622 #
# Purpose: Obtain IP address from hosts passed by stdin, calculate network of #
# each IP for eacho host, and find lowest IP for each network. #
####################################################################################
from sys import argv
from getpass import getpass, getuser
from subprocess import check_output
from os import devnull
from re import match
from pprint import PrettyPrinter
# Initialize global variables
FLAGS = ['-h', '--help', '-i', '-u', '-o', '-f', '--hosts']
USERNAME = getuser()
PUB_KEY = '~/.ssh/id_rsa.pub'
SUBNET_DICT = {}
OUT_LIST = []
RE_IP_PATTERN = '(10|172|192)\.[0-9][0-9]*[0-9]*\.[0-9][0-9]*[0-9]*\.[0-9][0-9]*[0-9]*\/[0-9][0-9]*'
# Print usage information and caveats
def print_help():
help_text = '''
USAGE
=====================================================================================
WARNING: You can use this script by just passing it hostnames from the command
line. But if you include any of the flags listed below you will need
to also include the the --hosts and pass the hostnames after it, or
include the -f flag and pass a file listing the hostnames after it.
NOTE: At its current state this script will only work with IPs of hosts in
/8, /16 and /24 networks. The plan is for it to eventually handle
other subnets as well.
-h or --help: Prints this message.
-i: Specify a private key to use to connect to remote machines. If left
blank will default to ~/.ssh/id_rsa.pub
-u: Specify USERNAME to use to connect to remotes machines.
-o: Specify an output file to save the information in.
-f: Specify a file with each hostname in a newline.
--hosts: Specify hostnames of servers separated by spaces.
'''
print(help_text)
return 0
# Initiates connection to remote host via ssh and returns IPs associated with host.
def ssh_to_host(cmd, hostname):
cmd_out = ''
host_ips = []
raw_ip_lines = []
err = open(devnull, 'w')
try:
cmd_out = check_output(cmd, shell=True, stderr=err)
raw_ip_lines = cmd_out.decode('utf-8').split('\n')
for line in raw_ip_lines:
if line == '':
continue
elif match(RE_IP_PATTERN, line.strip().split(' ')[1]): # Exclude loopback
host_ips.append(line.strip().split(' ')[1])
return host_ips
except:
print('Host {} is unreachable...'.format(hostname))
# Generate ssh command and pass to ssh_to_host
def get_host_ips(hostname):
cmd = "ssh -t -i {0} -o StrictHostKeyChecking=no {1}@{2} \
'export PATH=$PATH:/bin:/sbin:/usr/sbin;ip -4 addr \
| grep inet'".format(PUB_KEY, USERNAME, hostname)
return ssh_to_host(cmd, hostname)
# Find and add lowsest IP in list of IPs for a particular subnet. IP comparisson is
# done based on the relevant octect according to subnet CIDR notation.
def add_subnet(subnet, ip, split_ip, octet):
global SUBNET_DICT
if subnet not in SUBNET_DICT:
SUBNET_DICT[subnet] = {'ip_list': [ip], 'lowest_ip': ip}
else:
subnet_dict_split_ip = SUBNET_DICT[subnet]['lowest_ip'].split('.')
SUBNET_DICT[subnet]['ip_list'].append(ip)
if octet == 1:
if int(split_ip[octet]) - int(subnet_dict_split_ip[octet]) < 0:
SUBNET_DICT[subnet]['lowest_ip'] = ip
elif int(split_ip[octet]) - int(subnet_dict_split_ip[octet]) == 0:
if int(split_ip[octet + 1]) - int(subnet_dict_split_ip[octet + 1]) < 0:
SUBNET_DICT[subnet]['lowest_ip'] = ip
elif int(split_ip[octet + 1]) - int(subnet_dict_split_ip[octet + 1]) == 0:
if int(split_ip[octet + 2]) - int(subnet_dict_split_ip[octet + 2]) < 0:
SUBNET_DICT[subnet]['lowest_ip'] = ip
elif octet == 2:
if int(split_ip[octet]) - int(subnet_dict_split_ip[octet]) < 0:
SUBNET_DICT[subnet]['lowest_ip'] = ip
elif int(split_ip[octet]) - int(subnet_dict_split_ip[octet]) == 0:
if int(split_ip[octet + 1]) - int(subnet_dict_split_ip[octet + 1]) < 0:
SUBNET_DICT[subnet]['lowest_ip'] = ip
elif octet == 3:
if int(split_ip[octet]) - int(subnet_dict_split_ip[octet]) < 0:
SUBNET_DICT[subnet]['lowest_ip'] = ip
# Simple arithmetic operations to figure out which subnet host IPs belong to.
# Originally this this was meant to work with subnets other than just /8, /16 and /24
def calc_net_info(hostname):
powers_of_two = [128, 64, 32, 16, 8, 4, 2, 0]
ip = ''
cidr = 0
octet = 0
subnet = ''
split_ip = []
subnet_bits = 0
subnet_blk_size = 0
host_ips = get_host_ips(hostname)
if host_ips == None:
return 1
for entry in host_ips:
ip = entry.split('/')[0]
split_ip = ip.split('.')
cidr = int(entry.split('/')[1])
if cidr >= 8 and cidr < 16:
octet = 1
subnet_bits = cidr - 8
subnet_blk_size = 256 - sum(powers_of_two[:subnet_bits])
if int(split_ip[1]) <= subnet_blk_size:
subnet = '{0}.0.0.0/{1}'.format(split_ip[0], str(cidr))
add_subnet(subnet, ip, split_ip, octet)
elif cidr >= 16 and cidr < 24:
octet = 2
subnet_bits = cidr - 16
subnet_blk_size = 256 - sum(powers_of_two[:subnet_bits])
if int(split_ip[2]) <= subnet_blk_size:
subnet = '{0}.{1}.0.0/{2}'.format(split_ip[0],
split_ip[1],
str(cidr))
add_subnet(subnet, ip, split_ip, octet)
elif cidr >= 24 and cidr < 32:
octet = 3
subnet_bits = cidr - 24
subnet_blk_size = 256 - sum(powers_of_two[:subnet_bits])
if int(split_ip[3]) <= subnet_blk_size:
subnet = '{0}.{1}.{2}.0/{3}'.format(split_ip[0],
split_ip[1],
split_ip[2],
str(cidr))
add_subnet(subnet, ip, split_ip, octet)
def print_net_info():
global OUT_LIST
str_to_append = ''
for subnet in SUBNET_DICT:
out_str = '{0} - {1}'.format(subnet,
SUBNET_DICT[subnet]['lowest_ip'])
print(out_str)
if '-o' in argv:
OUT_LIST.append(out_str)
return 0
def main():
global USERNAME
global SSH_PW
global PUB_KEY
pp = PrettyPrinter()
argv_len = len(argv)
# Print help
if argv_len == 1:
print_help()
return 0
elif argv[1] == '-h' or argv[1] == '--help':
print_help()
return 0
# Check for username
if '-u' in argv:
idx = argv.index('-u')
if idx + 1 == argv_len:
print('Enter a USERNAME to use with ssh. Leave blank to use logged in user on current host.')
user = input('Username: ')
if user != '':
USERNAME = user
elif argv[idx + 1] in FLAGS:
print('Enter a USERNAME to use with ssh. Leave blank to use logged in user on current host.')
user = input('Username: ')
if user != '':
USERNAME = user
else:
USERNAME = argv[idx + 1]
# Check for ssh pub key
if '-i' not in argv:
print('No public ssh key specified...')
print('Using default in ~/.ssh...\n')
else:
idx = argv.index('-i')
if idx + 1 == argv_len:
print('No public ssh key specified. Leave blank to use default.')
pub_key = input('Path: ')
if pub_key != '':
PUB_KEY = pub_key
elif argv[idx + 1] in FLAGS:
print('No public ssh key specified. Leave blank to use default.')
pub_key = input('Path: ')
if pub_key != '':
PUB_KEY = pub_key
else:
PUB_KEY = argv[idx + 1]
# Calc net info for hostnames passed via cli with no other flags
if '--hosts' not in argv and '-f' not in argv:
for host in argv[1:]:
calc_net_info(host)
print_net_info()
# Calc net ifno for hostnames passed with --hosts or through a file
if '--hosts' in argv or '-f' in argv:
if '--hosts' in argv:
idx = argv.index('--hosts') + 1
if idx < argv_len and argv[idx] not in FLAGS:
while(idx < argv_len and argv[idx] not in FLAGS):
calc_net_info(argv[idx])
idx += 1
else:
print('No hostnames were passed...')
return 1
if '-f' in argv:
idx = argv.index('-f') + 1
if idx < argv_len and argv[idx] not in FLAGS:
hosts_file = open(argv[idx], 'r')
hosts = hosts_file.readlines()
for host in hosts:
calc_net_info(host.strip())
else:
print('No hostnames were passed...')
return 1
print_net_info()
if '-o' in argv:
idx = argv.index('-o') + 1
if argv[idx] != '' and argv[idx] not in FLAGS:
with open(argv[idx], 'w') as write_line:
for line in OUT_LIST:
write_line.write(line)
write_line.write('\n')
else:
print('Failed to write to {}'.format(argv[idx]))
return 1
# Uncomment to print dictionary for debuggin purposes.
#pp.pprint(SUBNET_DICT)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment