工作中需要对地址包含关系进行测试,现有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'])
二,实现的功能:
Host—>Network—>AddressGroup,低级别向高级别支持in测试
Network/AddressGroup同类之间支持<=测试
1)Host in Network,Host in AddressGroup测试
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,Network可以是通配符
Host('192.12.1.5') in AddressGroup(['192.168.1.0 255.255.255.0', '10.1.1.1']) # True
2)Network in AddressGroup测试
Network(192.168.1.0 255.255.255.0) in AddressGroup(['192.168.0.0 255.255.0.0', '10.1.1.1'])
3)Network <= Network测试
Network(192.168.1.0 255.255.255.0) <= Network('192.168.0.0 255.255.0.0') # True
Network(192.0.1.0 255.0.255.0) <= Network('192.168.0.0 255.255.0.0') # False,Network可以是通配符
4)AddressGroup <= AddressGroup测试
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
5)前缀修正
n1 = Network('192.168.1.0 255.0.255.0')
n1.network # 192.0.1.0
三,由于数据源都是标准的地址格式,因此很多细节和出错控制未考虑:
Address('300.1000.2.1') # 未做地址或者网段的出错控制,仅控制了split('.')是否等于4以及元素是否是整数
AddressGroup含C类host明细时,与所属AddressGroup网段进行eq比较时会不等
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 __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 return False def __eq__(self, other): try: return self._network == other._network and self._netmask == other._netmask except AttributeError: return NotImplemented def __le__(self, other): if isinstance(other, Network): parts = [i[0] & i[1] for i in zip(self._network, other._netmask)] if parts == other._network and all([i[0] >= i[1] for i in zip(self._netmask, other._netmask)]): return True return False def __repr__(self): return '<Network:{!r}>'.format(self._address) @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,因为abc.Set中两个Set长度相等直接返回Fasle,本类不一定,因此需要重构__lt__特殊方法 """ def __init__(self, components): self._components = list(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 <= 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))