Last active
September 8, 2018 23:12
-
-
Save welchbj/3ba236b093348a818a47cc29b68bce6d to your computer and use it in GitHub Desktop.
IPv4 variable-length subnet generation script
This file contains hidden or 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
| """Script for generating IPv4 subnets.""" | |
| import functools | |
| import sys | |
| from argparse import ( | |
| ArgumentParser, | |
| RawTextHelpFormatter) | |
| from collections import ( | |
| namedtuple) | |
| from ipaddress import ( | |
| ip_network, | |
| IPv4Address) | |
| from math import ( | |
| ceil, | |
| log2) | |
| Ipv4Subnet = namedtuple('Ipv4Subnet', [ | |
| 'num_hosts', | |
| 'increment', | |
| 'network_address', | |
| 'network_mask', | |
| 'first_host', | |
| 'last_host', | |
| 'broadcast_address', | |
| 'cidr']) | |
| print_err = functools.partial(print, file=sys.stderr) | |
| """Helper for printing to stderr.""" | |
| try: | |
| from texttable import Texttable | |
| except ImportError: | |
| print_err('Missing dependency `texttable`. Install it with::\n' | |
| 'pip install texttable') | |
| def get_parsed_args(args=None): | |
| """Get the parsed command-line arguments.""" | |
| parser = ArgumentParser( | |
| prog='subnet.py', | |
| description='Never subnet another IPv4 network by hand', | |
| formatter_class=RawTextHelpFormatter) | |
| parser.add_argument( | |
| '--net-id', | |
| action='store', | |
| required=True, | |
| help='the network ID to subnet') | |
| parser.add_argument( | |
| '--detailed', | |
| action='store_true', | |
| required=False, | |
| default=False, | |
| help='print more detailed information about the generated subnet(s)') | |
| parser.add_argument( | |
| '--num-hosts', | |
| action='store', | |
| required=True, | |
| type=int, | |
| nargs='+', | |
| help='the sizes with which to partition the specified network') | |
| if args is None: | |
| args = sys.argv[1:] | |
| return parser.parse_args(args) | |
| def gen_subnets(net_id, num_hosts_list): | |
| """Generate the subnets corresponding to each specified size.""" | |
| num_hosts_list = sorted(num_hosts_list, reverse=True) | |
| subnets = [] | |
| curr_starting_net_addr = net_id.network_address | |
| for num_hosts in num_hosts_list: | |
| num_host_bits = ceil(log2(num_hosts + 2)) | |
| num_net_bits = 32 - num_host_bits | |
| increment = 2**num_host_bits | |
| next_net_addr = curr_starting_net_addr + increment | |
| curr_net_addr = ip_network( | |
| str(curr_starting_net_addr) + '/' + | |
| str(num_net_bits)) | |
| subnets.append(Ipv4Subnet( | |
| num_hosts, | |
| increment, | |
| curr_net_addr.network_address, | |
| curr_net_addr.netmask, | |
| curr_net_addr.network_address + 1, | |
| next_net_addr - 2, | |
| next_net_addr - 1, | |
| num_net_bits | |
| )) | |
| curr_starting_net_addr = next_net_addr | |
| return subnets | |
| def main(): | |
| """Main routine for this script, returning the exit status code.""" | |
| try: | |
| opts = get_parsed_args() | |
| net_id = ip_network(opts.net_id) | |
| detailed = opts.detailed | |
| num_hosts_list = opts.num_hosts | |
| subnets = gen_subnets(net_id, num_hosts_list) | |
| if detailed: | |
| rows = [[ | |
| '# Hosts', | |
| 'Increment', | |
| 'Network ID', | |
| 'Subnet Mask', | |
| 'First Host', | |
| 'Last Host', | |
| 'Broadcast', | |
| 'CIDR' | |
| ]] | |
| for subnet in subnets: | |
| rows.append([ | |
| subnet.num_hosts, | |
| IPv4Address(subnet.increment), | |
| subnet.network_address, | |
| subnet.network_mask, | |
| subnet.first_host, | |
| subnet.last_host, | |
| subnet.broadcast_address, | |
| subnet.cidr | |
| ]) | |
| else: | |
| rows = [[ | |
| '# Hosts', | |
| 'Network ID', | |
| 'Subnet Mask', | |
| 'Broadcast']] | |
| for subnet in subnets: | |
| rows.append([ | |
| subnet.num_hosts, | |
| subnet.network_address, | |
| subnet.network_mask, | |
| subnet.broadcast_address | |
| ]) | |
| table = Texttable(max_width=0) | |
| table.add_rows(rows) | |
| print(table.draw()) | |
| except ValueError as e: | |
| print_err('Invalid argument received:', e) | |
| return 1 | |
| return 0 | |
| if __name__ == '__main__': | |
| sys.exit(main()) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example usage: