Python - recursively find lists in lists (n deep)
<p>I'm trying to use ipaddress (<a href="https://docs.python.org/3/library/ipaddress.html" rel="nofollow">https://docs.python.org/3/library/ipaddress.html</a>) to solve the following issue:</p>

<p>I have a BGP aggregate-address of 10.76.32.0/20</p>

<p>from which I would like to subtract all the network commands on the same router to figure out if the aggregate-address is larger than the defined networks and there is free network space left over.</p>

<p>The networks in this case are:</p>

<p>10.76.32.0/24
10.76.33.0/24
10.76.34.0/24
10.76.35.0/24
10.76.36.0/24
10.76.37.0/24
10.76.38.0/24
10.76.39.0/24
10.76.40.0/24
10.76.41.0/24
10.76.42.0/24
10.76.43.0/24
10.76.44.0/25
10.76.44.128/25
10.76.45.0/24
10.76.46.0/24
10.76.47.240/30
10.96.208.219/32</p>

<p>Using ipaddress.address_exclude(network) I can subtract a network from the aggregate-address and the operation returns an iterable object.</p>

<p>I could then make a for loop on this iterable object and see if it contains any of the other networks but in case it does, I will get yet another iterable object as the result. This process could hypothetically return lists within lists to an unspecified depth.</p>

<p>My question would be if anyone knows of a way to find all elements in such a structure of lists within lists that go to an arbitrary depth.</p>

<p>Hunor</p>

<p>Edit: Thanks for the answer ÐÐ½Ð´ÑÐµÐ¹ ÐÐµÐ½ÑÐºÐ¾Ð²ÑÐºÐ¸Ð¹. I didn't have time to check it out yet but I did manage to find a way to do it without using recursion with the .issubset method from netaddr:</p>

<pre><code>def undeclared_in_aggregate(aggregate, network):
    remaining = []
    composite = []
    for a_cidr in aggregate:
        a_cidr = IPSet([a_cidr])
        for n_cidr in network:
            n_cidr = IPSet([n_cidr])
            if n_cidr.issubset(a_cidr):
                a_cidr.remove(n_cidr.iprange())
                remaining = re.sub(r'IPSet\(\[|\]\)|\'', '', str(a_cidr))
                remaining = remaining.split(',')
                if remaining == ['']:
                    remaining = []
        composite += remaining
    return composite
</code></pre>

<p>this would take the aggregate-address and networks in list format and return the difference as a new list (per each aggregate-address).</p>

<p>for the example above:
['10.76.47.0/25', ' 10.76.47.128/26', ' 10.76.47.192/27', ' 10.76.47.224/28', ' 10.76.47.244/30', ' 10.76.47.248/29']</p>

<p>You didn't indicate the version of python (2 or 3) you are using so I assumed it's python3 based on the fact that the link you posted is to the python3 documentation. You didn't post any code, but based on your description of the issue I assume you need something like this:</p>

<pre><code>from ipaddress import ip_network, summarize_address_range
from itertools import chain

def exclude_multiple(super_network, networks):
    networks = sorted(networks, key=lambda n:n.network_address)
    starts = (n.broadcast_address + 1 for n in networks)
    starts = chain([super_network.network_address], starts)
    ends = (n.network_address - 1 for n in networks)
    ends = chain(ends, [super_network.broadcast_address])
    return ((s, e) for s, e in zip(starts, ends) if s != e + 1)  # s-1 != (e+1) - 1

networks = [
 '10.76.32.0/24',
 '10.76.33.0/24',
 # more here
]
networks = map(ip_network, networks)
super_network = ip_network('10.76.32.0/20')
free_ranges = exclude_multiple(super_network, networks)
free_networks = (summarize_address_range(f, l) for f, l in free_ranges)
free_networks = list(chain.from_iterable(free_networks))
</code></pre>

<p>This code doesn't include range checking so if the <code>networks</code> overlap or go outside the <code>super_network</code> you'll get a no very useful error from <code>summarize_address_range</code> but the range checking can be added with just a few tweaks.</p>

python networking recursion