ISTM that this is dangerously close to a set([...]) of CIDR blocks.
Not sure if we could easily work in the desired semantics by using a
set() instance alone, so CIDRGroup might be a good way to go.(subclass
set?)
>>>> IP(addr) in CIDRGroup(block1, block2, ...)
>
> I would envisage using addition on CIDR objects to combine two
> *adjacent* CIDR blocks of the same size, returning a new larger CIDR
> and (importantly) only where it logically makes sense. For example,
> you couldn't combined 2 CIDRs of different size such as
> CIDR('192.168.0.0/24') and CIDR('192.168.1.0/25') into a new single
> CIDR. This would also allow us to define a subtraction operator as
> well in following pseudocode examples :-
+1
> Addition
>
>>>> cidr = CIDR('192.168.0.0/24') + CIDR('192.168.1.0/24')
>>>> str(cidr)
> '192.168.0.0/23'
>
> Subtraction
>
>>>> cidr_plus = CIDR('192.168.0.0/23') - CIDR('192.168.1.0/24')
>>>> str(cidr_plus)
> '192.168.0.0/24'
>
>>>> cidr_minus = CIDR('192.168.0.0/23') - CIDR('192.168.0.0/24')
>>>> str(cidr_minus)
> '192.168.1.0/24'
+1, very nice.
What should happen if they are not adjacent? Exception?
Yeah, I think so. Hrm.. maybe an exception with a note to use the group class...
Or maybe even better, for non-adjacent blocks, instead of returning a
CIDR object switch internally and return a CIDR group object.
d
At first I liked this idea and starting considering some potential
corollaries. However, I feel that if you want a CIDRGroup, you should
create one. That is, if __add__ is to return either CIDRGroup or CIDR
then you can only rely on the intersection of the interfaces in order
to use it in a transparent manner(sure, you'll get ``__contains__``
and ``__iter__``(?), but I can't imagine much beyond that).
ISTM that it would be better to be explicit:
cidr1 + cidr2 = cidr3 where type(cidr3) always equals CIDR
cidrg = CIDRGroup([cidr1, cidr2])
I would imagine that most of the time people would just end up using
CIDRGroup.
Also, curious, what are the semantics of CIDRGroup? Can a group have
adjacent CIDRs? If the answer is yes, then I think it would be
desirable to include a reduce() method in order to simplify the group
as much as possible... Furthermore, would the only difference between
CIDRGroup and set([cidr1, ..., cidrN]) be a convenient interface(not
that there is anything wrong with a convenient interface ;)?
Personally, I think it would be useful to automatically conjoin
adjacent CIDRs to allow for presumed reductions. This would also help
make a clear, semantic distinction between a CIDRGroup and a simple
set([]) of CIDRs. The win being further justification for CIDRGroup's
existence. :)
hrm, perhaps I should have looked at the other project that implements
cidrgroup before I wrote this, but I'm having fun musing, sooo... :)
hrm. I've never been fond of relying on those kind of decorations
personally, but certainly it should be kept in mind. It does come in
handy from time to time...
However, with that, would we allow duplicate CIDRs in a CIDRGroup? If
no, how do we choose the one to use when presented with a choice in
the creation of a new CIDRGroup:
>>> p1 = netaddr.address.CIDR('192.168.0.0/16')
>>> p1.my_descriptor = 'my network!'
>>> p2 = netaddr.address.CIDR('192.168.0.0/16')
>>> p2.name = 'private network'
>>>
>>> g = netaddr.address.CIDRGroup([p1, p2])
But I digress; I'm luke warm on my thought(auto-reductions) anyways.
Really, a ``reduce()`` method returning the reduced variant seems
better as it would give you the ability to compare and contrast:
>>> c1=netaddr.CIDR('10.32.0.0/12')
>>> c2=netaddr.CIDR('10.48.0.0/12')
>>> c1.first()
netaddr.address.IP('10.32.0.0/32')
>>> c2.last()
netaddr.address.IP('10.63.255.255/32')
>>>
>>> cn=netaddr.CIDR('10.32.0.0/11')
>>> cn.first()
netaddr.address.IP('10.32.0.0/32')
>>> cn.last()
netaddr.address.IP('10.63.255.255/32')
So:
>>> cg = netaddr.CIDRGroup(['10.32.0.0/12', '10.48.0.0/12'])
>>> cg
netaddr.CIDRGroup(['10.32.0.0/12', '10.48.0.0/12'])
>>> cg.reduce()
netaddr.CIDRGroup(['10.32.0.0/11'])
I imagine this would be an important feature to have when dealing with
set operations(except,union,intersection)...
Hrm, I think we would at least have to(want to?) protect against
overlapping blocks....
> One property I would like to see in such a class would be some kind of
> tree (or trie) based binary search to get away from strictly O(N)
> membership tests where you have potentially nestable CIDRs (routing
> tables spring to mind here).
Aye.
> C) Forget __add__ on CIDR altogether and go for a class that acts as a
> container implementing __contains__ and __iter__.
>
> >>> cg = CIDRGroup(['192.168.0.0/24'), CIDR('192.168.2.0/24'),
> CIDR('224.0.0.0/4')])
> >>> CIDR('192.168.0.128/25') in cg
> True
> >>> IP('239.0.0.1') in cg
> True
>
> An iterator would flatten and yield all IPs in the ranges. This might
> be quite useful but end users could probably just roll their own
> easily enough with whatever features they required
+1 on the class, but, IIUC, -1 on the default __iter__ flattening as
it would likely eliminate--at least make more difficult--the
possibility of natural re-creation without condition:
>>> cg = CIDRGroup(['192.168.0/24', '192.168.25/24'])
>>> cg2 = CIDRGroup(cg)
Sure, we could make an explicit condition checking for CIDRGroup in
__init__, but why if we don't have to? :)
This isn't saying that flattening out the IPs isn't useful, it's
saying that it would probably make a nice generator method:
class CIDRGroup():
...
def ips(self):
'yield all the IPs of all the CIDRs in the group'
for x in self:
for ip in x:
yield ip
Additionally:
def has_ip(self, addr):
'check if the IP is in any of the CIDRs in the group'
...
> D) Go for a full blown AddrRangeSet(set) object.
>
> I don't know about you, but this gives me nightmares!
Sounds interesting. :)
> A), B), C), D) or something we haven't yet considered?
>
> I guess I'm just having trouble justifying the effort without (very
> selfishly) having my own use case ;-)
Well, I do have a use case for detecting and conjoining adjacent
CIDRs, so I may try to take a stab at a patch real soon now.
> We probably need to thrash this out a bit more (what a long post this
> is).
Loving this subject, btw.. :)