diff --git a/README.md b/README.md index 055ad67..a829d15 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ WebVirtCloud is a virtualization web interface for admins and users. It can dele ## Quick Install with Installer (Beta) -Install an OS and run specified commands. Installer supported OSes: Ubuntu 18.04, Debian 10, Centos/OEL/RHEL 8. +Install an OS and run specified commands. Installer supported OSes: Ubuntu 18.04/20.04, Debian 10/11, Centos/OEL/RHEL 8. It can be installed on a virtual machine, physical host or on a KVM host. ```bash diff --git a/vrtManager/IPy.py b/vrtManager/IPy.py index 163010b..3cddf29 100644 --- a/vrtManager/IPy.py +++ b/vrtManager/IPy.py @@ -6,128 +6,131 @@ Further Information might be available at: https://github.com/haypo/python-ipy """ -__version__ = "1.00" +__version__ = '1.01' import bisect -import collections -import sys import types +try: + import collections.abc as collections_abc +except ImportError: + import collections as collections_abc # Definition of the Ranges for IPv4 IPs # this should include www.iana.org/assignments/ipv4-address-space # and www.iana.org/assignments/multicast-addresses IPv4ranges = { - "0": "PUBLIC", # fall back - "00000000": "PRIVATE", # 0/8 - "00001010": "PRIVATE", # 10/8 - "0110010001": "CARRIER_GRADE_NAT", # 100.64/10 - "01111111": "LOOPBACK", # 127.0/8 - "1": "PUBLIC", # fall back - "1010100111111110": "PRIVATE", # 169.254/16 - "101011000001": "PRIVATE", # 172.16/12 - "1100000010101000": "PRIVATE", # 192.168/16 - "111": "RESERVED", # 224/3 -} + '0': 'PUBLIC', # fall back + '00000000': 'PRIVATE', # 0/8 + '00001010': 'PRIVATE', # 10/8 + '0110010001': 'CARRIER_GRADE_NAT', #100.64/10 + '01111111': 'LOOPBACK', # 127.0/8 + '1': 'PUBLIC', # fall back + '1010100111111110': 'PRIVATE', # 169.254/16 + '101011000001': 'PRIVATE', # 172.16/12 + '1100000010101000': 'PRIVATE', # 192.168/16 + '111': 'RESERVED', # 224/3 + } # Definition of the Ranges for IPv6 IPs # http://www.iana.org/assignments/ipv6-address-space/ # http://www.iana.org/assignments/ipv6-unicast-address-assignments/ # http://www.iana.org/assignments/ipv6-multicast-addresses/ IPv6ranges = { - "00000000": "RESERVED", # ::/8 - "0" * 96: "RESERVED", # ::/96 Formerly IPV4COMP [RFC4291] - "0" * 128: "UNSPECIFIED", # ::/128 - "0" * 127 + "1": "LOOPBACK", # ::1/128 - "0" * 80 + "1" * 16: "IPV4MAP", # ::ffff:0:0/96 - "00000000011001001111111110011011" + "0" * 64: "WKP46TRANS", # 0064:ff9b::/96 Well-Known-Prefix [RFC6052] - "00000001": "UNASSIGNED", # 0100::/8 - "0000001": "RESERVED", # 0200::/7 Formerly NSAP [RFC4048] - "0000010": "RESERVED", # 0400::/7 Formerly IPX [RFC3513] - "0000011": "RESERVED", # 0600::/7 - "00001": "RESERVED", # 0800::/5 - "0001": "RESERVED", # 1000::/4 - "001": "GLOBAL-UNICAST", # 2000::/3 [RFC4291] - "00100000000000010000000": "SPECIALPURPOSE", # 2001::/23 [RFC4773] - "00100000000000010000000000000000": "TEREDO", # 2001::/32 [RFC4380] - "00100000000000010000000000000010" + "0" * 16: "BMWG", # 2001:0002::/48 Benchmarking [RFC5180] - "0010000000000001000000000001": "ORCHID", # 2001:0010::/28 (Temp until 2014-03-21) [RFC4843] - "00100000000000010000001": "ALLOCATED APNIC", # 2001:0200::/23 - "00100000000000010000010": "ALLOCATED ARIN", # 2001:0400::/23 - "00100000000000010000011": "ALLOCATED RIPE NCC", # 2001:0600::/23 - "00100000000000010000100": "ALLOCATED RIPE NCC", # 2001:0800::/23 - "00100000000000010000101": "ALLOCATED RIPE NCC", # 2001:0a00::/23 - "00100000000000010000110": "ALLOCATED APNIC", # 2001:0c00::/23 - "00100000000000010000110110111000": "DOCUMENTATION", # 2001:0db8::/32 [RFC3849] - "00100000000000010000111": "ALLOCATED APNIC", # 2001:0e00::/23 - "00100000000000010001001": "ALLOCATED LACNIC", # 2001:1200::/23 - "00100000000000010001010": "ALLOCATED RIPE NCC", # 2001:1400::/23 - "00100000000000010001011": "ALLOCATED RIPE NCC", # 2001:1600::/23 - "00100000000000010001100": "ALLOCATED ARIN", # 2001:1800::/23 - "00100000000000010001101": "ALLOCATED RIPE NCC", # 2001:1a00::/23 - "0010000000000001000111": "ALLOCATED RIPE NCC", # 2001:1c00::/22 - "00100000000000010010": "ALLOCATED RIPE NCC", # 2001:2000::/20 - "001000000000000100110": "ALLOCATED RIPE NCC", # 2001:3000::/21 - "0010000000000001001110": "ALLOCATED RIPE NCC", # 2001:3800::/22 - "0010000000000001001111": "RESERVED", # 2001:3c00::/22 Possible future allocation to RIPE NCC - "00100000000000010100000": "ALLOCATED RIPE NCC", # 2001:4000::/23 - "00100000000000010100001": "ALLOCATED AFRINIC", # 2001:4200::/23 - "00100000000000010100010": "ALLOCATED APNIC", # 2001:4400::/23 - "00100000000000010100011": "ALLOCATED RIPE NCC", # 2001:4600::/23 - "00100000000000010100100": "ALLOCATED ARIN", # 2001:4800::/23 - "00100000000000010100101": "ALLOCATED RIPE NCC", # 2001:4a00::/23 - "00100000000000010100110": "ALLOCATED RIPE NCC", # 2001:4c00::/23 - "00100000000000010101": "ALLOCATED RIPE NCC", # 2001:5000::/20 - "0010000000000001100": "ALLOCATED APNIC", # 2001:8000::/19 - "00100000000000011010": "ALLOCATED APNIC", # 2001:a000::/20 - "00100000000000011011": "ALLOCATED APNIC", # 2001:b000::/20 - "0010000000000010": "6TO4", # 2002::/16 "6to4" [RFC3056] - "001000000000001100": "ALLOCATED RIPE NCC", # 2003::/18 - "001001000000": "ALLOCATED APNIC", # 2400::/12 - "001001100000": "ALLOCATED ARIN", # 2600::/12 - "00100110000100000000000": "ALLOCATED ARIN", # 2610::/23 - "00100110001000000000000": "ALLOCATED ARIN", # 2620::/23 - "001010000000": "ALLOCATED LACNIC", # 2800::/12 - "001010100000": "ALLOCATED RIPE NCC", # 2a00::/12 - "001011000000": "ALLOCATED AFRINIC", # 2c00::/12 - "00101101": "RESERVED", # 2d00::/8 - "0010111": "RESERVED", # 2e00::/7 - "0011": "RESERVED", # 3000::/4 - "010": "RESERVED", # 4000::/3 - "011": "RESERVED", # 6000::/3 - "100": "RESERVED", # 8000::/3 - "101": "RESERVED", # a000::/3 - "110": "RESERVED", # c000::/3 - "1110": "RESERVED", # e000::/4 - "11110": "RESERVED", # f000::/5 - "111110": "RESERVED", # f800::/6 - "1111110": "ULA", # fc00::/7 [RFC4193] - "111111100": "RESERVED", # fe00::/9 - "1111111010": "LINKLOCAL", # fe80::/10 - "1111111011": "RESERVED", # fec0::/10 Formerly SITELOCAL [RFC4291] - "11111111": "MULTICAST", # ff00::/8 - "1111111100000001": "NODE-LOCAL MULTICAST", # ff01::/16 - "1111111100000010": "LINK-LOCAL MULTICAST", # ff02::/16 - "1111111100000100": "ADMIN-LOCAL MULTICAST", # ff04::/16 - "1111111100000101": "SITE-LOCAL MULTICAST", # ff05::/16 - "1111111100001000": "ORG-LOCAL MULTICAST", # ff08::/16 - "1111111100001110": "GLOBAL MULTICAST", # ff0e::/16 - "1111111100001111": "RESERVED MULTICAST", # ff0f::/16 - "111111110011": "PREFIX-BASED MULTICAST", # ff30::/12 [RFC3306] - "111111110111": "RP-EMBEDDED MULTICAST", # ff70::/12 [RFC3956] -} + '00000000' : 'RESERVED', # ::/8 + '0' * 96 : 'RESERVED', # ::/96 Formerly IPV4COMP [RFC4291] + '0' * 128 : 'UNSPECIFIED', # ::/128 + '0' * 127 + '1' : 'LOOPBACK', # ::1/128 + '0' * 80 + '1' * 16 : 'IPV4MAP', # ::ffff:0:0/96 + '00000000011001001111111110011011' + '0' * 64 : 'WKP46TRANS', # 0064:ff9b::/96 Well-Known-Prefix [RFC6052] + '00000001' : 'UNASSIGNED', # 0100::/8 + '0000001' : 'RESERVED', # 0200::/7 Formerly NSAP [RFC4048] + '0000010' : 'RESERVED', # 0400::/7 Formerly IPX [RFC3513] + '0000011' : 'RESERVED', # 0600::/7 + '00001' : 'RESERVED', # 0800::/5 + '0001' : 'RESERVED', # 1000::/4 + '001' : 'GLOBAL-UNICAST', # 2000::/3 [RFC4291] + '00100000000000010000000' : 'SPECIALPURPOSE', # 2001::/23 [RFC4773] + '00100000000000010000000000000000' : 'TEREDO', # 2001::/32 [RFC4380] + '00100000000000010000000000000010' + '0' * 16 : 'BMWG', # 2001:0002::/48 Benchmarking [RFC5180] + '0010000000000001000000000001' : 'ORCHID', # 2001:0010::/28 (Temp until 2014-03-21) [RFC4843] + '00100000000000010000001' : 'ALLOCATED APNIC', # 2001:0200::/23 + '00100000000000010000010' : 'ALLOCATED ARIN', # 2001:0400::/23 + '00100000000000010000011' : 'ALLOCATED RIPE NCC', # 2001:0600::/23 + '00100000000000010000100' : 'ALLOCATED RIPE NCC', # 2001:0800::/23 + '00100000000000010000101' : 'ALLOCATED RIPE NCC', # 2001:0a00::/23 + '00100000000000010000110' : 'ALLOCATED APNIC', # 2001:0c00::/23 + '00100000000000010000110110111000' : 'DOCUMENTATION', # 2001:0db8::/32 [RFC3849] + '00100000000000010000111' : 'ALLOCATED APNIC', # 2001:0e00::/23 + '00100000000000010001001' : 'ALLOCATED LACNIC', # 2001:1200::/23 + '00100000000000010001010' : 'ALLOCATED RIPE NCC', # 2001:1400::/23 + '00100000000000010001011' : 'ALLOCATED RIPE NCC', # 2001:1600::/23 + '00100000000000010001100' : 'ALLOCATED ARIN', # 2001:1800::/23 + '00100000000000010001101' : 'ALLOCATED RIPE NCC', # 2001:1a00::/23 + '0010000000000001000111' : 'ALLOCATED RIPE NCC', # 2001:1c00::/22 + '00100000000000010010' : 'ALLOCATED RIPE NCC', # 2001:2000::/20 + '001000000000000100110' : 'ALLOCATED RIPE NCC', # 2001:3000::/21 + '0010000000000001001110' : 'ALLOCATED RIPE NCC', # 2001:3800::/22 + '0010000000000001001111' : 'RESERVED', # 2001:3c00::/22 Possible future allocation to RIPE NCC + '00100000000000010100000' : 'ALLOCATED RIPE NCC', # 2001:4000::/23 + '00100000000000010100001' : 'ALLOCATED AFRINIC', # 2001:4200::/23 + '00100000000000010100010' : 'ALLOCATED APNIC', # 2001:4400::/23 + '00100000000000010100011' : 'ALLOCATED RIPE NCC', # 2001:4600::/23 + '00100000000000010100100' : 'ALLOCATED ARIN', # 2001:4800::/23 + '00100000000000010100101' : 'ALLOCATED RIPE NCC', # 2001:4a00::/23 + '00100000000000010100110' : 'ALLOCATED RIPE NCC', # 2001:4c00::/23 + '00100000000000010101' : 'ALLOCATED RIPE NCC', # 2001:5000::/20 + '0010000000000001100' : 'ALLOCATED APNIC', # 2001:8000::/19 + '00100000000000011010' : 'ALLOCATED APNIC', # 2001:a000::/20 + '00100000000000011011' : 'ALLOCATED APNIC', # 2001:b000::/20 + '0010000000000010' : '6TO4', # 2002::/16 "6to4" [RFC3056] + '001000000000001100' : 'ALLOCATED RIPE NCC', # 2003::/18 + '001001000000' : 'ALLOCATED APNIC', # 2400::/12 + '001001100000' : 'ALLOCATED ARIN', # 2600::/12 + '00100110000100000000000' : 'ALLOCATED ARIN', # 2610::/23 + '00100110001000000000000' : 'ALLOCATED ARIN', # 2620::/23 + '001010000000' : 'ALLOCATED LACNIC', # 2800::/12 + '001010100000' : 'ALLOCATED RIPE NCC', # 2a00::/12 + '001011000000' : 'ALLOCATED AFRINIC', # 2c00::/12 + '00101101' : 'RESERVED', # 2d00::/8 + '0010111' : 'RESERVED', # 2e00::/7 + '0011' : 'RESERVED', # 3000::/4 + '010' : 'RESERVED', # 4000::/3 + '011' : 'RESERVED', # 6000::/3 + '100' : 'RESERVED', # 8000::/3 + '101' : 'RESERVED', # a000::/3 + '110' : 'RESERVED', # c000::/3 + '1110' : 'RESERVED', # e000::/4 + '11110' : 'RESERVED', # f000::/5 + '111110' : 'RESERVED', # f800::/6 + '1111110' : 'ULA', # fc00::/7 [RFC4193] + '111111100' : 'RESERVED', # fe00::/9 + '1111111010' : 'LINKLOCAL', # fe80::/10 + '1111111011' : 'RESERVED', # fec0::/10 Formerly SITELOCAL [RFC4291] + '11111111' : 'MULTICAST', # ff00::/8 + '1111111100000001' : 'NODE-LOCAL MULTICAST', # ff01::/16 + '1111111100000010' : 'LINK-LOCAL MULTICAST', # ff02::/16 + '1111111100000100' : 'ADMIN-LOCAL MULTICAST', # ff04::/16 + '1111111100000101' : 'SITE-LOCAL MULTICAST', # ff05::/16 + '1111111100001000' : 'ORG-LOCAL MULTICAST', # ff08::/16 + '1111111100001110' : 'GLOBAL MULTICAST', # ff0e::/16 + '1111111100001111' : 'RESERVED MULTICAST', # ff0f::/16 + '111111110011' : 'PREFIX-BASED MULTICAST', # ff30::/12 [RFC3306] + '111111110111' : 'RP-EMBEDDED MULTICAST', # ff70::/12 [RFC3956] + } -MAX_IPV4_ADDRESS = 0xFFFFFFFF -MAX_IPV6_ADDRESS = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -IPV6_TEST_MAP = 0xFFFFFFFFFFFFFFFFFFFFFFFF00000000 -IPV6_MAP_MASK = 0x00000000000000000000FFFF00000000 +MAX_IPV4_ADDRESS = 0xffffffff +MAX_IPV6_ADDRESS = 0xffffffffffffffffffffffffffffffff +IPV6_TEST_MAP = 0xffffffffffffffffffffffff00000000 +IPV6_MAP_MASK = 0x00000000000000000000ffff00000000 -if sys.version_info >= (3,): +try: + INT_TYPES = (int, long) + STR_TYPES = (str, unicode) + xrange +except NameError: INT_TYPES = (int,) STR_TYPES = (str,) xrange = range -else: - INT_TYPES = (int, long) - STR_TYPES = (str, unicode) class IPint(object): @@ -202,7 +205,7 @@ class IPint(object): elif isinstance(data, STR_TYPES): # TODO: refactor me! # splitting of a string into IP and prefixlen et. al. - x = data.split("-") + x = data.split('-') if len(x) == 2: # a.b.c.0-a.b.c.255 specification ? (ip, last) = x @@ -219,10 +222,10 @@ class IPint(object): # make sure the broadcast is the same as the last ip # otherwise it will return /16 for something like: # 192.168.0.0-192.168.191.255 - if IP("%s/%s" % (ip, 32 - netbits)).broadcast().int() != last: + if IP('%s/%s' % (ip, 32-netbits)).broadcast().int() != last: raise ValueError("the range %s is not on a network boundary." % data) elif len(x) == 1: - x = data.split("/") + x = data.split('/') # if no prefix is given use defaults if len(x) == 1: ip = x[0] @@ -231,7 +234,7 @@ class IPint(object): raise ValueError("only one '/' allowed in IP Address") else: (ip, prefixlen) = x - if prefixlen.find(".") != -1: + if prefixlen.find('.') != -1: # check if the user might have used a netmask like # a.b.c.d/255.255.255.0 (netmask, vers) = parseAddress(prefixlen) @@ -255,7 +258,8 @@ class IPint(object): if make_net: self.ip = self.ip & _prefixlenToNetmask(self._prefixlen, self._ipversion) - if not _checkNetaddrWorksWithPrefixlen(self.ip, self._prefixlen, self._ipversion): + if not _checkNetaddrWorksWithPrefixlen(self.ip, + self._prefixlen, self._ipversion): raise ValueError("%s has invalid prefix length (%s)" % (repr(self), self._prefixlen)) else: raise TypeError("Unsupported data type: %s" % type(data)) @@ -313,12 +317,13 @@ class IPint(object): want == 3 -lastip 1.2.3.0-1.2.3.255 """ - if (self._ipversion == 4 and self._prefixlen == 32) or (self._ipversion == 6 and self._prefixlen == 128): + if (self._ipversion == 4 and self._prefixlen == 32) or \ + (self._ipversion == 6 and self._prefixlen == 128): if self.NoPrefixForSingleIp: want = 0 - if want is None: + if want == None: want = self.WantPrefixLen - if want is None: + if want == None: want = 1 if want: if want == 2: @@ -333,7 +338,7 @@ class IPint(object): # default return "/%d" % (self._prefixlen) else: - return "" + return '' # We have different flavours to convert to: # strFullsize 127.0.0.1 2001:0658:022a:cafe:0200:c0ff:fe8d:08fa @@ -342,7 +347,7 @@ class IPint(object): # strHex 0x7F000001 0x20010658022ACAFE0200C0FFFE8D08FA # strDec 2130706433 42540616829182469433547974687817795834 - def strBin(self, wantprefixlen=None): + def strBin(self, wantprefixlen = None): """Return a string representation as a binary value. >>> print(IP('127.0.0.1').strBin()) @@ -352,12 +357,12 @@ class IPint(object): """ bits = _ipVersionToLen(self._ipversion) - if self.WantPrefixLen is None and wantprefixlen is None: + if self.WantPrefixLen == None and wantprefixlen == None: wantprefixlen = 0 ret = _intToBin(self.ip) - return "0" * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen) + return '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen) - def strCompressed(self, wantprefixlen=None): + def strCompressed(self, wantprefixlen = None): """Return a string representation in compressed format using '::' Notation. >>> IP('127.0.0.1').strCompressed() @@ -368,18 +373,18 @@ class IPint(object): 'ffff:ffff:ffff:ffff:ffff:f:f:fffc/127' """ - if self.WantPrefixLen is None and wantprefixlen is None: + if self.WantPrefixLen == None and wantprefixlen == None: wantprefixlen = 1 if self._ipversion == 4: return self.strFullsize(wantprefixlen) else: - if self.ip >> 32 == 0xFFFF: + if self.ip >> 32 == 0xffff: ipv4 = intToIp(self.ip & MAX_IPV4_ADDRESS, 4) text = "::ffff:" + ipv4 + self._printPrefix(wantprefixlen) return text # find the longest sequence of '0' - hextets = [int(x, 16) for x in self.strFullsize(0).split(":")] + hextets = [int(x, 16) for x in self.strFullsize(0).split(':')] # every element of followingzeros will contain the number of zeros # following the corresponding element of hextets followingzeros = [0] * 8 @@ -390,19 +395,19 @@ class IPint(object): if max(followingzeros) > 1: # genererate string with the longest number of zeros cut out # now we need hextets as strings - hextets = [x for x in self.strNormal(0).split(":")] - while compressionpos < len(hextets) and hextets[compressionpos] == "0": - del hextets[compressionpos] - hextets.insert(compressionpos, "") + hextets = [x for x in self.strNormal(0).split(':')] + while compressionpos < len(hextets) and hextets[compressionpos] == '0': + del(hextets[compressionpos]) + hextets.insert(compressionpos, '') if compressionpos + 1 >= len(hextets): - hextets.append("") + hextets.append('') if compressionpos == 0: - hextets = [""] + hextets - return ":".join(hextets) + self._printPrefix(wantprefixlen) + hextets = [''] + hextets + return ':'.join(hextets) + self._printPrefix(wantprefixlen) else: return self.strNormal(0) + self._printPrefix(wantprefixlen) - def strNormal(self, wantprefixlen=None): + def strNormal(self, wantprefixlen = None): """Return a string representation in the usual format. >>> print(IP('127.0.0.1').strNormal()) @@ -411,19 +416,21 @@ class IPint(object): 2001:658:22a:cafe:200:0:0:1 """ - if self.WantPrefixLen is None and wantprefixlen is None: + if self.WantPrefixLen == None and wantprefixlen == None: wantprefixlen = 1 if self._ipversion == 4: ret = self.strFullsize(0) elif self._ipversion == 6: - ret = ":".join(["%x" % x for x in [int(x, 16) for x in self.strFullsize(0).split(":")]]) + ret = ':'.join(["%x" % x for x in [int(x, 16) for x in self.strFullsize(0).split(':')]]) else: raise ValueError("only IPv4 and IPv6 supported") + + return ret + self._printPrefix(wantprefixlen) - def strFullsize(self, wantprefixlen=None): + def strFullsize(self, wantprefixlen = None): """Return a string representation in the non-mangled format. >>> print(IP('127.0.0.1').strFullsize()) @@ -432,12 +439,12 @@ class IPint(object): 2001:0658:022a:cafe:0200:0000:0000:0001 """ - if self.WantPrefixLen is None and wantprefixlen is None: + if self.WantPrefixLen == None and wantprefixlen == None: wantprefixlen = 1 return intToIp(self.ip, self._ipversion) + self._printPrefix(wantprefixlen) - def strHex(self, wantprefixlen=None): + def strHex(self, wantprefixlen = None): """Return a string representation in hex format in lower case. >>> print(IP('127.0.0.1').strHex()) @@ -446,13 +453,13 @@ class IPint(object): 0x20010658022acafe0200000000000001 """ - if self.WantPrefixLen is None and wantprefixlen is None: + if self.WantPrefixLen == None and wantprefixlen == None: wantprefixlen = 0 - x = "0x%x" % self.ip + x = '0x%x' % self.ip return x + self._printPrefix(wantprefixlen) - def strDec(self, wantprefixlen=None): + def strDec(self, wantprefixlen = None): """Return a string representation in decimal format. >>> print(IP('127.0.0.1').strDec()) @@ -461,10 +468,10 @@ class IPint(object): 42540616829182469433547762482097946625 """ - if self.WantPrefixLen is None and wantprefixlen is None: + if self.WantPrefixLen == None and wantprefixlen == None: wantprefixlen = 0 - x = "%d" % self.ip + x = '%d' % self.ip return x + self._printPrefix(wantprefixlen) def iptype(self): @@ -499,6 +506,7 @@ class IPint(object): return iprange[bits[:i]] return "unknown" + def netmask(self): """Return netmask as an integer. @@ -512,6 +520,7 @@ class IPint(object): return ((2 ** self._prefixlen) - 1) << locallen + def strNetmask(self): """Return netmask as an string. Mostly useful for IPv6. @@ -543,6 +552,7 @@ class IPint(object): locallen = bits - self._prefixlen return 2 ** locallen + def __nonzero__(self): """All IPy objects should evaluate to true in boolean context. Ordinarily they do, but if handling a default route expressed as @@ -579,14 +589,16 @@ class IPint(object): raise ValueError("Only adjacent networks can be added together.") ret = IP(self.int(), ipversion=self._ipversion) ret._prefixlen = self.prefixlen() - 1 - if not _checkNetaddrWorksWithPrefixlen(ret.ip, ret._prefixlen, ret._ipversion): - raise ValueError("The resulting %s has invalid prefix length (%s)" % (repr(ret), ret._prefixlen)) + if not _checkNetaddrWorksWithPrefixlen(ret.ip, ret._prefixlen, + ret._ipversion): + raise ValueError("The resulting %s has invalid prefix length (%s)" + % (repr(ret), ret._prefixlen)) return ret def __sub__(self, other): """Return the prefixes that are in this IP but not in the other""" - return _remove_subprefix(self, other) - + return _remove_subprefix(self, other) + def __getitem__(self, key): """Called to implement evaluation of self[key]. @@ -619,6 +631,8 @@ class IPint(object): return self.ip + int(key) + + def __contains__(self, item): """Called to implement membership test operators. @@ -645,6 +659,7 @@ class IPint(object): else: return False + def overlaps(self, item): """Check if two IP address ranges overlap. @@ -670,6 +685,7 @@ class IPint(object): else: return 0 + def __str__(self): """Dispatch to the prefered String Representation. @@ -677,6 +693,7 @@ class IPint(object): return self.strCompressed() + def __repr__(self): """Print a representation of the Object. @@ -688,23 +705,24 @@ class IPint(object): IP('10.0.0.0/24') """ - return "IPint('%s')" % (self.strCompressed(1)) + return("IPint('%s')" % (self.strCompressed(1))) + def __cmp__(self, other): """Called by comparison operations. Should return a negative integer if self < other, zero if self == other, a positive integer if self > other. - + Order is first determined by the address family. IPv4 addresses are always smaller than IPv6 addresses: - + >>> IP('10.0.0.0') < IP('2001:db8::') 1 - + Then the first address is compared. Lower addresses are always smaller: - + >>> IP('10.0.0.0') > IP('10.0.0.1') 0 >>> IP('10.0.0.0/24') > IP('10.0.0.1') @@ -715,10 +733,10 @@ class IPint(object): 1 >>> IP('10.0.1.0/24') > IP('10.0.0.0') 1 - + Then the prefix length is compared. Shorter prefixes are considered smaller than longer prefixes: - + >>> IP('10.0.0.0/24') > IP('10.0.0.0') 0 >>> IP('10.0.0.0/24') > IP('10.0.0.0/25') @@ -729,19 +747,19 @@ class IPint(object): """ if not isinstance(other, IPint): raise TypeError - + # Lower version -> lower result if self._ipversion != other._ipversion: return self._ipversion < other._ipversion and -1 or 1 - + # Lower start address -> lower result if self.ip != other.ip: return self.ip < other.ip and -1 or 1 - + # Shorter prefix length -> lower result if self._prefixlen != other._prefixlen: return self._prefixlen < other._prefixlen and -1 or 1 - + # No differences found return 0 @@ -773,7 +791,7 @@ class IPint(object): thehash = int(-1) ip = self.ip while ip > 0: - thehash = thehash ^ (ip & 0x7FFFFFFF) + thehash = thehash ^ (ip & 0x7fffffff) ip = ip >> 32 thehash = thehash ^ self._prefixlen return int(thehash) @@ -807,17 +825,17 @@ class IP(IPint): >>> IP('10.0.0.0/8').netmask() IP('255.0.0.0') - """ + """ return IP(IPint.netmask(self), ipversion=self._ipversion) def _getIPv4Map(self): if self._ipversion != 6: return None - if (self.ip >> 32) != 0xFFFF: + if (self.ip >> 32) != 0xffff: return None ipv4 = self.ip & MAX_IPV4_ADDRESS if self._prefixlen != 128: - ipv4 = "%s/%s" % (ipv4, 32 - (128 - self._prefixlen)) + ipv4 = '%s/%s' % (ipv4, 32-(128-self._prefixlen)) return IP(ipv4, ipversion=4) def reverseNames(self): @@ -846,17 +864,17 @@ class IP(IPint): if self._ipversion == 4: ret = [] # TODO: Refactor. Add support for IPint objects - if self.len() < 2 ** 8: + if self.len() < 2**8: for x in self: ret.append(x.reverseName()) - elif self.len() < 2 ** 16: - for i in xrange(0, self.len(), 2 ** 8): + elif self.len() < 2**16: + for i in xrange(0, self.len(), 2**8): ret.append(self[i].reverseName()[2:]) - elif self.len() < 2 ** 24: - for i in xrange(0, self.len(), 2 ** 16): + elif self.len() < 2**24: + for i in xrange(0, self.len(), 2**16): ret.append(self[i].reverseName()[4:]) else: - for i in xrange(0, self.len(), 2 ** 24): + for i in xrange(0, self.len(), 2**24): ret.append(self[i].reverseName()[6:]) return ret elif self._ipversion == 6: @@ -868,7 +886,7 @@ class IP(IPint): raise NotImplementedError("can't create IPv6 reverse names at sub nibble level") s = list(s) s.reverse() - s = ".".join(s) + s = '.'.join(s) first_nibble_index = int(32 - (self._prefixlen // 4)) * 2 return ["%s.ip6.arpa." % s[first_nibble_index:]] else: @@ -893,34 +911,31 @@ class IP(IPint): if self._ipversion == 4: s = self.strFullsize(0) - s = s.split(".") + s = s.split('.') s.reverse() first_byte_index = int(4 - (self._prefixlen // 8)) if self._prefixlen % 8 != 0: - nibblepart = "%s-%s" % ( - s[3 - (self._prefixlen // 8)], - intToIp(self.ip + self.len() - 1, 4).split(".")[-1], - ) - nibblepart += "." + nibblepart = "%s-%s" % (s[3-(self._prefixlen // 8)], intToIp(self.ip + self.len() - 1, 4).split('.')[-1]) + nibblepart += '.' else: nibblepart = "" - s = ".".join(s[first_byte_index:]) + s = '.'.join(s[first_byte_index:]) return "%s%s.in-addr.arpa." % (nibblepart, s) elif self._ipversion == 6: ipv4 = self._getIPv4Map() if ipv4 is not None: return ipv4.reverseName() - s = "%032x" % self.ip + s = '%032x' % self.ip if self._prefixlen % 4 != 0: - nibblepart = "%s-%x" % (s[self._prefixlen :], self.ip + self.len() - 1) - nibblepart += "." + nibblepart = "%s-%x" % (s[self._prefixlen:], self.ip + self.len() - 1) + nibblepart += '.' else: nibblepart = "" s = list(s) s.reverse() - s = ".".join(s) + s = '.'.join(s) first_nibble_index = int(32 - (self._prefixlen // 4)) * 2 return "%s%s.ip6.arpa." % (nibblepart, s[first_nibble_index:]) else: @@ -935,9 +950,9 @@ class IP(IPint): >>> print(IP('127.0.0.1').make_net('255.0.0.0')) 127.0.0.0/8 """ - if "/" in str(netmask): + if '/' in str(netmask): raise ValueError("invalid netmask (%s)" % netmask) - return IP("%s/%s" % (self, netmask), make_net=True) + return IP('%s/%s' % (self, netmask), make_net=True) def __getitem__(self, key): """Called to implement evaluation of self[key]. @@ -966,7 +981,7 @@ class IP(IPint): IP('10.0.0.0/8') """ - return "IP('%s')" % (self.strCompressed(1)) + return("IP('%s')" % (self.strCompressed(1))) def get_mac(self): """ @@ -978,15 +993,15 @@ class IP(IPint): """ if self._ipversion != 6: return None - if (self.ip & 0x20000FFFF000000) != 0x20000FFFE000000: + if (self.ip & 0x20000ffff000000) != 0x20000fffe000000: return None - return "%02x:%02x:%02x:%02x:%02x:%02x" % ( - (((self.ip >> 56) & 0xFF) & 0xFD), - (self.ip >> 48) & 0xFF, - (self.ip >> 40) & 0xFF, - (self.ip >> 16) & 0xFF, - (self.ip >> 8) & 0xFF, - self.ip & 0xFF, + return '%02x:%02x:%02x:%02x:%02x:%02x' % ( + (((self.ip >> 56) & 0xff) & 0xfd), + (self.ip >> 48) & 0xff, + (self.ip >> 40) & 0xff, + (self.ip >> 16) & 0xff, + (self.ip >> 8) & 0xff, + self.ip & 0xff, ) def v46map(self): @@ -1001,32 +1016,34 @@ class IP(IPint): IP('192.168.1.1') """ if self._ipversion == 4: - return IP(str(IPV6_MAP_MASK + self.ip) + "/%s" % (self._prefixlen + 96)) + return IP(str(IPV6_MAP_MASK + self.ip) + + "/%s" % (self._prefixlen + 96)) else: if self.ip & IPV6_TEST_MAP == IPV6_MAP_MASK: - return IP(str(self.ip - IPV6_MAP_MASK) + "/%s" % (self._prefixlen - 96)) - raise ValueError("%s cannot be converted to an IPv4 address." % repr(self)) + return IP(str(self.ip - IPV6_MAP_MASK) + + "/%s" % (self._prefixlen - 96)) + raise ValueError("%s cannot be converted to an IPv4 address." + % repr(self)) - -class IPSet(collections.MutableSet): +class IPSet(collections_abc.MutableSet): def __init__(self, iterable=[]): # Make sure it's iterable, otherwise wrap - if not isinstance(iterable, collections.Iterable): + if not isinstance(iterable, collections_abc.Iterable): raise TypeError("'%s' object is not iterable" % type(iterable).__name__) - + # Make sure we only accept IP objects for prefix in iterable: if not isinstance(prefix, IP): - raise ValueError("Only IP objects can be added to an IPSet") - + raise ValueError('Only IP objects can be added to an IPSet') + # Store and optimize self.prefixes = iterable[:] self.optimize() - + def __contains__(self, ip): valid_masks = self.prefixtable.keys() if isinstance(ip, IP): - # Don't dig through more-specific ranges + #Don't dig through more-specific ranges ip_mask = ip._prefixlen valid_masks = [x for x in valid_masks if x <= ip_mask] for mask in sorted(valid_masks): @@ -1039,19 +1056,19 @@ class IPSet(collections.MutableSet): def __iter__(self): for prefix in self.prefixes: yield prefix - + def __len__(self): return self.len() - + def __add__(self, other): return IPSet(self.prefixes + other.prefixes) - + def __sub__(self, other): new = IPSet(self.prefixes) for prefix in other: new.discard(prefix) return new - + def __and__(self, other): left = iter(self.prefixes) right = iter(other.prefixes) @@ -1078,30 +1095,30 @@ class IPSet(collections.MutableSet): return IPSet(result) def __repr__(self): - return "%s([" % self.__class__.__name__ + ", ".join(map(repr, self.prefixes)) + "])" - + return '%s([' % self.__class__.__name__ + ', '.join(map(repr, self.prefixes)) + '])' + def len(self): return sum(prefix.len() for prefix in self.prefixes) def add(self, value): # Make sure it's iterable, otherwise wrap - if not isinstance(value, collections.Iterable): + if not isinstance(value, collections_abc.Iterable): value = [value] - + # Check type for prefix in value: if not isinstance(prefix, IP): - raise ValueError("Only IP objects can be added to an IPSet") - + raise ValueError('Only IP objects can be added to an IPSet') + # Append and optimize self.prefixes.extend(value) self.optimize() - + def discard(self, value): # Make sure it's iterable, otherwise wrap - if not isinstance(value, collections.Iterable): + if not isinstance(value, collections_abc.Iterable): value = [value] - + # This is much faster than iterating over the addresses if isinstance(value, IPSet): value = value.prefixes @@ -1109,8 +1126,8 @@ class IPSet(collections.MutableSet): # Remove for del_prefix in value: if not isinstance(del_prefix, IP): - raise ValueError("Only IP objects can be removed from an IPSet") - + raise ValueError('Only IP objects can be removed from an IPSet') + # First check if this prefix contains anything in our list found = False d = 0 @@ -1119,19 +1136,19 @@ class IPSet(collections.MutableSet): self.prefixes.pop(i - d) d = d + 1 found = True - + if found: # If the prefix was bigger than an existing prefix, then it's # certainly not a subset of one, so skip the rest continue - + # Maybe one of our prefixes contains this prefix found = False for i in range(len(self.prefixes)): if del_prefix in self.prefixes[i]: - self.prefixes[i : i + 1] = self.prefixes[i] - del_prefix + self.prefixes[i:i+1] = self.prefixes[i] - del_prefix break - + self.optimize() def isdisjoint(self, other): @@ -1160,22 +1177,22 @@ class IPSet(collections.MutableSet): while i < addrlen: # Everything that might be inside this prefix follows # directly behind it - j = i + 1 + j = i+1 while j < addrlen and self.prefixes[j] in self.prefixes[i]: # Mark for deletion by overwriting with None self.prefixes[j] = None j += 1 - + # Continue where we left off i = j - + # Try to merge as many prefixes as possible run_again = True while run_again: # Filter None values. This happens when a subset is eliminated # above, or when two prefixes are merged below self.prefixes = [a for a in self.prefixes if a is not None] - + # We'll set run_again to True when we make changes that require # re-evaluation of the whole list run_again = False @@ -1184,9 +1201,9 @@ class IPSet(collections.MutableSet): # prefix length and differ only on the last bit of the prefix addrlen = len(self.prefixes) i = 0 - while i < addrlen - 1: + while i < addrlen-1: j = i + 1 - + try: # The next line will throw an exception when merging # is not possible @@ -1207,7 +1224,6 @@ class IPSet(collections.MutableSet): except KeyError: self.prefixtable[address._prefixlen] = [address] - def _parseAddressIPv6(ipstr): """ Internal function used by parseAddress() to parse IPv6 address with ':'. @@ -1274,16 +1290,16 @@ def _parseAddressIPv6(ipstr): fill_pos = len(items) index += 2 continue - pos = text.find(":") + pos = text.find(':') if pos == 0: # Invalid IPv6, eg. '1::2:' raise ValueError("%r: Invalid IPv6 address" % ipstr) if pos != -1: items.append(text[:pos]) - if text[pos : pos + 2] == "::": + if text[pos:pos+2] == "::": index += pos else: - index += pos + 1 + index += pos+1 if index == len(ipstr): # Invalid IPv6, eg. '1::2:' @@ -1292,13 +1308,13 @@ def _parseAddressIPv6(ipstr): items.append(text) break - if items and "." in items[-1]: + if items and '.' in items[-1]: # IPv6 ending with IPv4 like '::ffff:192.168.0.1' - if (fill_pos is not None) and not (fill_pos <= len(items) - 1): + if (fill_pos is not None) and not (fill_pos <= len(items)-1): # Invalid IPv6: 'ffff:192.168.0.1::' raise ValueError("%r: Invalid IPv6 address: '::' after IPv4" % ipstr) value = parseAddress(items[-1])[0] - items = items[:-1] + ["%04x" % (value >> 16), "%04x" % (value & 0xFFFF)] + items = items[:-1] + ["%04x" % (value >> 16), "%04x" % (value & 0xffff)] # Expand fill_pos to fill with '0' # ['1','2'] with fill_pos=1 => ['1', '0', '0', '0', '0', '0', '0', '2'] @@ -1306,7 +1322,7 @@ def _parseAddressIPv6(ipstr): diff = 8 - len(items) if diff <= 0: raise ValueError("%r: Invalid IPv6 address: '::' is not needed" % ipstr) - items = items[:fill_pos] + ["0"] * diff + items[fill_pos:] + items = items[:fill_pos] + ['0']*diff + items[fill_pos:] # Here we have a list of 8 strings if len(items) != 8: @@ -1319,7 +1335,7 @@ def _parseAddressIPv6(ipstr): for item in items: try: item = int(item, 16) - error = not (0 <= item <= 0xFFFF) + error = not(0 <= item <= 0xffff) except ValueError: error = True if error: @@ -1328,7 +1344,6 @@ def _parseAddressIPv6(ipstr): index += 1 return value - def parseAddress(ipstr, ipversion=0): """ Parse a string and return the corresponding IP address (as integer) @@ -1383,7 +1398,7 @@ def parseAddress(ipstr, ipversion=0): except ValueError: intval = None - if ipstr.startswith("0x") and hexval is not None: + if ipstr.startswith('0x') and hexval is not None: if hexval > MAX_IPV6_ADDRESS: raise ValueError("IP Address can't be larger than %x: %x" % (MAX_IPV6_ADDRESS, hexval)) if hexval <= MAX_IPV4_ADDRESS: @@ -1391,19 +1406,19 @@ def parseAddress(ipstr, ipversion=0): else: return (hexval, 6) - if ipstr.find(":") != -1: + if ipstr.find(':') != -1: return (_parseAddressIPv6(ipstr), 6) elif len(ipstr) == 32 and hexval is not None: # assume IPv6 in pure hexadecimal notation return (hexval, 6) - elif ipstr.find(".") != -1 or (intval is not None and intval < 256 and ipversion != 6): + elif ipstr.find('.') != -1 or (intval is not None and intval < 256 and ipversion != 6): # assume IPv4 ('127' gets interpreted as '127.0.0.0') - bytes = ipstr.split(".") + bytes = ipstr.split('.') if len(bytes) > 4: raise ValueError("IPv4 Address with more than 4 bytes") - bytes += ["0"] * (4 - len(bytes)) + bytes += ['0'] * (4 - len(bytes)) bytes = [int(x) for x in bytes] for x in bytes: if x > 255 or x < 0: @@ -1433,12 +1448,12 @@ def intToIp(ip, version): if ip < 0: raise ValueError("IPs can't be negative: %d" % (ip)) - ret = "" + ret = '' if version == 4: if ip > MAX_IPV4_ADDRESS: raise ValueError("IPv4 Address can't be larger than %x: %x" % (MAX_IPV4_ADDRESS, ip)) for l in xrange(4): - ret = str(ip & 0xFF) + "." + ret + ret = str(ip & 0xff) + '.' + ret ip = ip >> 8 ret = ret[:-1] elif version == 6: @@ -1448,14 +1463,13 @@ def intToIp(ip, version): for x in xrange(1, 33): ret = l[-x] + ret if x % 4 == 0: - ret = ":" + ret + ret = ':' + ret ret = ret[1:] else: raise ValueError("only IPv4 and IPv6 supported") return ret - def _ipVersionToLen(version): """Return number of bits in address for a certain IP version. @@ -1489,25 +1503,10 @@ def _countFollowingZeros(l): return 1 + _countFollowingZeros(l[1:]) -_BitTable = { - "0": "0000", - "1": "0001", - "2": "0010", - "3": "0011", - "4": "0100", - "5": "0101", - "6": "0110", - "7": "0111", - "8": "1000", - "9": "1001", - "a": "1010", - "b": "1011", - "c": "1100", - "d": "1101", - "e": "1110", - "f": "1111", -} - +_BitTable = {'0': '0000', '1': '0001', '2': '0010', '3': '0011', + '4': '0100', '5': '0101', '6': '0110', '7': '0111', + '8': '1000', '9': '1001', 'a': '1010', 'b': '1011', + 'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'} def _intToBin(val): """Return the binary representation of an integer as string.""" @@ -1515,15 +1514,14 @@ def _intToBin(val): if val < 0: raise ValueError("Only positive values allowed") s = "%x" % val - ret = "" + ret = '' for x in s: ret += _BitTable[x] # remove leading zeros - while ret[0] == "0" and len(ret) > 1: + while ret[0] == '0' and len(ret) > 1: ret = ret[1:] return ret - def _count1Bits(num): """Find the highest bit set to 1 in an integer.""" ret = 0 @@ -1532,7 +1530,6 @@ def _count1Bits(num): ret += 1 return ret - def _count0Bits(num): """Find the highest bit set to 0 in an integer.""" @@ -1575,7 +1572,7 @@ def _checkPrefix(ip, prefixlen, version): zbits = bits + 1 else: zbits = _count0Bits(ip) - if zbits < bits - prefixlen: + if zbits < bits - prefixlen: return 0 else: return 1 @@ -1604,7 +1601,7 @@ def _checkNetmask(netmask, masklen): def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version): """Check if a base addess of a network is compatible with a prefixlen""" try: - return net & _prefixlenToNetmask(prefixlen, version) == net + return (net & _prefixlenToNetmask(prefixlen, version) == net) except ValueError: return False @@ -1632,22 +1629,22 @@ def _prefixlenToNetmask(prefixlen, version): return 0 elif prefixlen < 0: raise ValueError("Prefixlen must be > 0") - return ((2 << prefixlen - 1) - 1) << (_ipVersionToLen(version) - prefixlen) + return ((2<