Last active
June 25, 2018 03:28
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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