Skip to content

Instantly share code, notes, and snippets.

@welchbj
Last active September 8, 2018 23:12
Show Gist options
  • Select an option

  • Save welchbj/3ba236b093348a818a47cc29b68bce6d to your computer and use it in GitHub Desktop.

Select an option

Save welchbj/3ba236b093348a818a47cc29b68bce6d to your computer and use it in GitHub Desktop.
IPv4 variable-length subnet generation script
"""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())
@welchbj
Copy link
Author

welchbj commented Aug 24, 2018

Example usage:

$ python subnet.py --net-id 10.10.0.0/16 --num-hosts 4000 500 200 120
+---------+------------+-----------------+--------------+
| # Hosts | Network ID |   Subnet Mask   |  Broadcast   |
+=========+============+=================+==============+
| 4000    | 10.10.0.0  | 255.255.240.0   | 10.10.15.255 |
+---------+------------+-----------------+--------------+
| 500     | 10.10.16.0 | 255.255.254.0   | 10.10.17.255 |
+---------+------------+-----------------+--------------+
| 200     | 10.10.18.0 | 255.255.255.0   | 10.10.18.255 |
+---------+------------+-----------------+--------------+
| 120     | 10.10.19.0 | 255.255.255.128 | 10.10.19.127 |
+---------+------------+-----------------+--------------+

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment