|
NetAddrFAQ
Frequently Asked Questions
Frequently Asked QuestionsUpdated for release 0.6
Does the IP() class represent an IP address or a subnet and why is there a separate CIDR() class?Short answerThe IP() class represents an individual IP address and the CIDR() class represents a subnet, network or VLAN. Subnet representation is the main remit of the CIDR() class. The support for CIDR subnet prefixes (x.x.x.x/y) via the IP() constructor is mainly for user convenience and follows the Rule of Least Surprise for those coming to netaddr from other less Pythonic networking libraries. Longer (more verbose) answerThe netaddr module currently contains two major object groups :-
First, consider an analogy from basic Python analogy comparing operations on a string and a tuple. In [1]: s = 'foobar'
In [2]: t = ('foobar',)Iterating over a string yields individual characters. In [3]: [i for i in s] Out[3]: ['f', 'o', 'o', 'b', 'a', 'r'] Iterating over a tuple yields individual strings. In [4]: [i for i in t] Out[4]: ['foobar'] A tuple cannot be compared directly to a string. In [5]: s == t Out[5]: False To successfully compare them, the string and tuple need to be converted into the same terms. # Index the tuple. In [6]: s == t[0] Out[6]: True # Alternatively, convert the string into a tuple, result is the same. In [7]: (s,) == t Out[7]: True The string is anologous to how the scalar objects in netaddr behave and the tuple is analogous to how a sequence objects behave. Now, consider the behaviour of an IP() object and a CIDR() object performing the same operations shown above. Iterating over and IP() object yields octets (not other IP addresses as you might expect). In [1]: from netaddr import IP, CIDR
In [2]: [i for i in IP('192.168.0.0/30')]
Out[2]: [192, 168, 0, 0]Iterating over a CIDR() object yields IP() objects (as you might expect). In [3]: [i for i in CIDR('192.168.0.0/30')]
Out[3]:
[IP('192.168.0.0'),
IP('192.168.0.1'),
IP('192.168.0.2'),
IP('192.168.0.3')]In netaddr an IP() object is not equivalent to a CIDR() object (even a /32 CIDR). In [4]: IP('192.168.0.1/32') == CIDR('192.168.0.1/32')
Out[4]: FalseCompare objects in IP terms by accessing an IP() object from within our CIDR() object. In [5]: IP('192.168.0.1/32') == CIDR('192.168.0.1/32')[0]
Out[5]: True
In [5]: IP('192.168.0.1/32') == CIDR('192.168.0.1/32').network
Out[5]: TrueCompare objects in CIDR terms by using the .cidr() method on the IP() object to return the equivalent CIDR() object for comparison. In [7]: IP('192.168.0.1/32').cidr() == CIDR('192.168.0.1/32')
Out[7]: TrueHere is the iteration on an IP() object using the CIDR call through method. In [8]: [i for i in IP('192.168.0.0/30').cidr()]
Out[8]:
[IP('192.168.0.0'),
IP('192.168.0.1'),
IP('192.168.0.2'),
IP('192.168.0.3')]How do I access the base (network) and broadcast addresses in a CIDR object?From release 0.6 onwards, you can now just use the .network and .broadcast properties. This is how you access them as individual IP objects. In [1]: from netaddr import CIDR
In [2]: cidr = CIDR('192.168.0.0/24')
In [3]: cidr.network, cidr.broadcast
Out[3]: (IP('192.168.0.0'), IP('192.168.0.255'))and as address strings :- In [4]: cidr.fmt = str
In [3]: cidr.network, cidr.broadcast
Out[3]: ('192.168.0.0', '192.168.0.255')In release 0.5 and earlier, this was the way to do it :- Use the list index (getitem) operation to access the address you require. It is not exposed as .network() and .broadcast() to keep the CIDR() object interface lean and free of cruft. In [1]: from netaddr import IP, CIDR
In [2]: cidr = CIDR('192.168.0.0/24')
In [3]: cidr[0]
Out[3]: netaddr.address.IP('192.168.0.0')You can access the broadcast address like this :- In [4]: cidr[-1]
Out[4]: netaddr.address.IP('192.168.0.255')Why do partial CIDRs 192, 192.168, 192.168.0 all have a /24 netmask?Good question. Calling the CIDR() constructor with a partial IPv4 address (n/a for IPv6) invokes the older classful IP (version 4) addressing rules to help figure out what the implicit subnet (CIDR prefix) is. Details can be found in various RFCs but here is the basic rule set :-
Why is feature x in netaddr slower than version x in a similar library?netaddr is under active development and this question is becoming more irrelevant with each new release. As a general rule netaddr follows the conventional wisdom that states :-
From release 0.6 onwards, a lot more emphasis is being placed on performance improvements as much of the core infrastructure code is now in place and working well. Here is an example of the vast speed improvements made so far :- netaddr version 0.5.x In [1]: import netaddr
In [2]: netaddr.__version__
Out[2]: '0.5.2'
In [3]: timeit netaddr.IP('1.1.1.1')
10000 loops, best of 3: 42 µs per loop
In [4]: timeit netaddr.IP('1.1.1.1/255.255.255.0')
10000 loops, best of 3: 146 µs per loop
In [5]: timeit netaddr.IP('fe80::/64')
10000 loops, best of 3: 68.4 µs per loop
In [6]: timeit netaddr.IP('1.1.1.1', netaddr.AT_INET)
10000 loops, best of 3: 34 µs per loop
In [7]: timeit netaddr.IP('fe80::/64', netaddr.AT_INET6)
10000 loops, best of 3: 59.2 µs per loopnetaddr version 0.6.x In [1]: import netaddr
In [2]: netaddr.__version__
Out[2]: '0.6'
In [3]: timeit netaddr.IP('1.1.1.1')
100000 loops, best of 3: 11.5 µs per loop
In [4]: timeit netaddr.IP('1.1.1.1/255.255.255.0')
10000 loops, best of 3: 53.7 µs per loop
In [5]: timeit netaddr.IP('fe80::/64')
10000 loops, best of 3: 38.8 µs per loop
In [6]: timeit netaddr.IP('1.1.1.1', netaddr.AT_INET)
100000 loops, best of 3: 8.9 µs per loop
In [7]: timeit netaddr.IP('fe80::/64', netaddr.AT_INET6)
10000 loops, best of 3: 27.2 µs per loopAs you can see in some cases there is now almost a 4-fold improvement! netaddr is not standing still and there are more improvements and features on the way. Stay tuned! |
- Does IP hold an ip or a subnet ? (IP('192.168.0.0/24') works fine, but IP('192.168.0.0/24').ipv4() forgets the netmask.)
- CIDR is not able to use ".ipv6()" nor ".ipv4()" why ?
- CIDR has a .netmask() or .prefixlen() to get the netmask in different format, but how to access the base IP (and not with a dirty getitem(0,...) )
CIDR('192') gets a netmask of 24, as of CIDR('192.168') and CIDR('192.168.0') ... the latter seems OK but not the other... is it normal ?
Covered comments above with FAQ updates and the following tickets :-
http://code.google.com/p/netaddr/issues/detail?id=17 http://code.google.com/p/netaddr/issues/detail?id=18
Thanks for help improving this project.
Because the broadcast address is actually a configurable property of a network, and will not always be the last address in the network, the CIDR.broadcast property should either be settable (via the constructor) or should not exist.