工作中需要对地址包含关系进行测试,现有ipaddress标准库和IPy无法满足,于是自己动手编写小脚本,主要实现== , in, <等专用功能,其他功能可以后续用到再补充,例如迭代打印网段内所有地址,输出前缀长度等等。
一,含有的类:
Host类:主机地址,eg, Host('192.168.1.1')
Network类:网络地址,eg,Network('192.168.1.0 255.255.255.0')
AddressGroup类:主机地址和网络地址组成的列表,eg,AddressGroup(['192.168.1.0 255.255.255.0', '10.1.1.1'])
二,可以实现的in测试功能:
1)地址与网段的测试,地址与前缀+通配符的测试:
Host('192.168.1.1') in Network('192.168.1.0 255.255.255.0') # True
Host('192.12.1.5') in Network('192.0.1.0 255.0.255.0') # True
2)网段与网段的测试,网段与地址+通配符的测试:
Network(192.168.1.0 255.255.255.0) in Network('192.168.0.0 255.255.0.0') # True
Network(192.0.1.0 255.0.255.0) in Network('192.168.0.0 255.255.0.0') # False
3)地址/网段组,与地址/网段组的测试:
AddressGroup(['192.168.1.0 255.255.255.0', '10.1.1.1']) < AddressGroup(['192.168.0.0 255.255.255.0', '10.1.1.1']) # True
4)前缀修正
n1 = Network('192.168.1.0 255.0.255.0')
n1.network # 192.0.1.0
三,由于数据源都是标准的地址格式,因此很多细节和出错控制未考虑:
Address('300.1000.2.1') # 未做地址或者网段的出错控制,仅控制了split('.')是否等于4以及元素是否是整数
class AddressValueError(ValueError): """A Value Error related to the netmask.""" def ip_address(address): try: return Host(address) except AddressValueError: pass try: return Network(address) except AddressValueError: pass raise AddressValueError('Not a valid host or network address: {!r}'.format(address)) def _host_int_from_string(address): octets = address.split('.') if len(octets) != 4: raise AddressValueError("Expected 4 octets in {!r}".format(address)) try: return [int(i) for i in octets] except ValueError as exc: raise AddressValueError("%s in %r" % (exc, address)) from None def _network_int_from_string(address): parts = address.split() if len(parts) != 2: raise AddressValueError("Expected network plus netmask in {!r}".format(address)) return _host_int_from_string(parts[0]), _host_int_from_string(parts[1]) class Host(object): """eg, address: 192.168.1.1""" __slots__ = ('_address', '_host') def __init__(self, address): self._address= address self._host = _host_int_from_string(address) def __repr__(self): return '<Host:{!r}>'.format(self._address) def __eq__(self, other): try: return self._address == other._address except AttributeError: return NotImplemented class Network(object): """eg, address: 192.168.1.0 255.255.255.0""" __slots__ = ('_address', '_network', '_netmask') def __init__(self, address): self._address = address self._network, self._netmask = _network_int_from_string(address) self._network = [i[0] & i[1] for i in zip(self._network, self._netmask)] def __repr__(self): return '<Network:{!r}>'.format(self._address) def __eq__(self, other): try: return self._network == other._network and self._netmask == other._netmask except AttributeError: return NotImplemented def __contains__(self, other): if isinstance(other, Host): network_other = [i[0] & i[1] for i in zip(other._host, self._netmask)] if self._network == network_other: return True elif isinstance(other, Network): parts = [i[0] & i[1] for i in zip(other._network, self._netmask)] if parts == self._network and all([i[1] >= i[0] for i in zip(self._netmask, other._netmask)]): return True return False @property def network(self): return '.'.join([str(i) for i in self._network]) class AddressGroup(object): """ eg,address_group: ['192.168.1.1', '192.168.1.2', '10.1.1.0 255.255.255.0'] 无法继承abc.Set自动获取lt方法(只要实现len/iter/contains),因为abc.Set中两个Set长度相等直接返回Fasle,本类不一定,因此需要自己编写__lt__特殊方法 """ def __init__(self, components): self._components = components self._compObjs = [ip_address(i) for i in components] def __len__(self): return len(self._components) def __iter__(self): for c in self._compObjs: yield c def __contains__(self, other): if isinstance(other, Host): for obj in self: cond1 = isinstance(obj, Host) and other == obj cond2 = isinstance(obj, Network) and other in obj if cond1 or cond2: return True if isinstance(other, Network): for obj in self: if isinstance(obj, Network) and other in obj: return True return False def __eq__(self, other): try: return sorted(self._components) == sorted(other._components) except AttributeError: return NotImplemented def __lt__(self, other): return all([i in other for i in self._compObjs]) def __repr__(self): return '<AddressGroup:{!r}>'.format(' | '.join(self._components))