hetznerctl: Fix PEP-8 errors E127 and E302.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
RobotError: Use __str__ for formatting the error.

On a traceback, the __str__ value will be read instead of __repr__ so we
get the name of the exception class, but not the actual exception.

This is now no longer the case :-)

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Add support for retrieving the reverse PTR.

This is only for _retrieving_ the reverse PTRs of single IP addresses
and doesn't support subnets yet.

Also, we're relying on try ... except here, which we really should
factor out someday in RobotConnection.request() to be handled without
reliance on exception handling.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Add utility functions for parsing IP addresses.

This is for parsing both IPv4 and IPv6 addresses and also distinguish
between them. The reason I'm using inet_pton() instead of the ipaddress
module is because it is only for Python 3.3 and we want to stay
backwards-compatible here.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Detect and show IP address types in subnets.

On hetznerctl show we just show the corresponding address type, but we
also save the numeric representation of the network and the gateway IP
address in the Subnet object.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
util: Add functions for getting IPv4/IPv6 ranges.

This allows to determine whether a specific IP address is valid for a
given subnet and to also iterate through all available addresses (haha,
have fun on IPv6).

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
util: Add functions to convert integers to addrs.

So far we've shovelled all addresses into numeric representation and
worked on that (which obviously makes sense because it makes operations
painless) but it may make sense to convert it back to the "human
readable" notation.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
util: Don't exclude broadcast addr in IPv4 range.

At Hetzner, the whole network is routed to your machine, so in theory
you can also assign the address that's usually reserved for broadcast to
be used as an unicast address.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
server/Subnet: Allow to retrieve the IP range.

This is to be used in preparation for setting reverse DNS PTRs for
subnets so we can not only allow for easy selection of addresses within
the subnet but also for checking whether an rPTR can be assigned to the
address.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
hetznerctl/show: Display IP address range.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
util.parse_ipaddr: Don't return tuple on is_ipv6.

This is quite redundant and is also unexpected behaviour when using the
function. The latter bit myself when it comes to the numeric gateway
address, which has been set to the resulting tuple instead of just the
bare numeric address.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
server: Fix Subnet.update_info() method.

If update_info() is called directly on the object instead of while being
constructed, the update_info() method doesn't succeed because it's
trying to fetch information about an IP address instead of a subnet.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
server: Add __contains__ method to Subnet.

This allows for easy checking of whether an IP address is valid within a
specific subnet simply by using something like:

'1.2.3.4' in server.subnets.get('1.2.3.0')

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
setup.py: Add util module to py_modules.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
setup.py: Integrate running doctests.

This recurses through all PYTHON_MODULES and runs doctest.testmod() on,
so it's nothing fancy.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
server: Allow to get an IpAddress from a Subnet.

It makes sense to reuse the IpAddress object for subnet ips as well,
especially because the robot API returns the same (except
"separate_mac") fields for subnet and ip.

This has the upside that we don't need to implement an additional
manager for stuff like reverse DNS and can just access the ReverseDNS
object with something like:

server.subnets.get('1.2.3.0').get_ip('1.2.3.4).rdns

Signed-off-by: aszlig <aszlig@redmoonstudios.org>