def subnet_reverse_zones(subnet): ''' :type subnet: Ipblock ''' rzones = [] subnet_str = IP(int(subnet.address), version=subnet.version).label(expanded=True) if subnet.version == 4: if subnet.prefix < 16: raise ValueError("The prefix %d is too small (the minimum is 16)" % subnet.prefix) raddr = [int(n) for n in list(reversed(subnet_str.split('.')))[1:]] bits = max(0, 24 - subnet.prefix) for i in range(2**bits): rzones.append('.'.join([str(n) for n in raddr] + ['in-addr.arpa'])) raddr[0] += 1 elif subnet.version == 6: # Don't allow prefixes larger than /64 prefix = min(subnet.prefix, 64) # fhn = first nibble which has host bits fhn_bits = prefix % 4 fhn_number = int(prefix / 4) if fhn_bits == 0: fhn_bits = 4 fhn_number -= 1 addr = list(reversed(subnet_str.replace(':', '')[:fhn_number + 1])) for i in range(2**(4 - fhn_bits)): rzones.append('.'.join(addr + ['ip6.arpa'])) addr[0] = '%x' % (int(addr[0], 16) + 1) else: raise ValueError("Unknown IP version") return rzones
def test_subnet_vlan(self): self.r.ipblock_create('16.0.0.0/8', status='Container') self.r.ippool_create("pool2", vlan=5) self.r.ippool_add_subnet('pool2', '16.0.0.0/24') self.r.ippool_create("pool", vlan=4) assert Ipblock.query_ip(IP('16.0.0.0/24'), None).one().vlan.vid == 5 self.r.ippool_add_subnet('pool', '16.0.0.0/24', allow_move=True) assert Ipblock.query_ip(IP('16.0.0.0/24'), None).one().vlan.vid == 4
def test_contains(): ip1 = IP('12.0.0.0/25') ip2 = IP('12.0.0.0/24') assert ip1 in ip2 assert ip2 not in ip1 assert ip1 in ip1 assert IP('12.0.0.2') in ip1
def test_create(self): self.r.ip_mark('12.0.0.1') self.r.ipblock_create('12.0.0.0/16', status='Container') self.r.ip_mark('12.0.0.2') self.r.ipblock_create('12.0.0.0/24', status='Container') self.r.ip_mark('12.0.0.3') assert get_ipblock('12.0.0.0/16').parent is None assert get_ipblock('12.0.0.0/24').parent.ip == IP('12.0.0.0/16') assert get_ipblock('12.0.0.1').parent.ip == IP('12.0.0.0/24') assert get_ipblock('12.0.0.2').parent.ip == IP('12.0.0.0/24') assert get_ipblock('12.0.0.3').parent.ip == IP('12.0.0.0/24') with raises(AlreadyExistsError): self.r.ipblock_create('12.0.0.0/16')
def test_parents(self): self.r.ipblock_create('192.168.0.0/16', status='Container') self.r.ip_mark('192.168.0.1') assert get_ipblock('192.168.0.1').parent.ip == IP('192.168.0.0/16') self.r.ipblock_create('192.168.0.0/24', status='Container') assert get_ipblock('192.168.0.1').parent.ip == IP('192.168.0.0/24') self.r.ipblock_remove('192.168.0.0/24', force=True) assert get_ipblock('192.168.0.1').parent.ip == IP('192.168.0.0/16') self.r.ipblock_create('192.168.0.0/24', status='Container') assert get_ipblock('192.168.0.1').parent.ip == IP('192.168.0.0/24')
def blocks_in_range(range_, prefix, total_bits, version): range_start, range_end = range_ numaddrs = 2**(total_bits - prefix) block_start = align(range_start, numaddrs) block_end = block_start + numaddrs - 1 while block_end <= range_end: yield IP(block_start, prefix, version) block_start = block_end + 1 block_end += numaddrs
def fill(range_start, range_end): ''' Fill the free space between range_start and range_end with the least amount of blocks. ''' if range_start >= range_end: return [] hb = max_hostbits(range_start, bits - self.prefix) while range_start + 2 ** hb - 1 > range_end: hb -= 1 return [IP(range_start, bits - hb, self.version)] + fill(range_start + 2 ** hb, range_end)
def name(row): if row.objclass == 'rr': return '%s %s %s' % (row.name, row.type, row.value) elif row.objclass == 'ipblock': return IP(int(row.address), row.prefix, row.version).label() elif row.objclass == 'groupright': return row.group elif row.objclass == 'key': return row.label else: return row.name
def check_ippool_add_subnet(self, pool, subnet, **options): self.r.ippool_add_subnet(pool, subnet, **options) pool = Pool.query.filter_by(name=pool).one() subnet = query_ip(subnet).one() gateway = None if 'gateway' in options: gateway = IP(options.get('gateway')).address assert subnet.pool == pool assert subnet.version == pool.version assert subnet.vlan == pool.vlan assert subnet.gateway == gateway self.assertEqual(dict((a.name.name, a.value) for a in subnet.attributes), options.get('attributes', {}))
def build_tree_mem(layer3domain, version): logging.debug('build_tree_mem(%s, %d)' % (layer3domain.name, version)) new_parents = [] blocks = db.session.query(Ipblock.id, Ipblock.address, Ipblock.prefix, Ipblock.parent_id)\ .filter(Ipblock.version == version, Ipblock.layer3domain_id == layer3domain.id)\ .order_by(Ipblock.prefix).all() tree = IPTrie(version) for id, address, prefix, parent in blocks: ip = IP(int(address), prefix, version) if prefix == tree._bits: new_parent = tree.parent(ip) else: new_parent = tree.insert(ip, id) new_parent_id = new_parent.data if new_parent else None if new_parent_id != parent: new_parents.append((id, new_parent_id)) return tree, new_parents
def free_ranges(parent_query): ''' Returns the list of ranges representing the free space in `parent_query` grouped by parent block. ''' parents = dict((p.id, IP(int(p.address), p.prefix, p.version)) for p in parent_query.all()) psq = parent_query.subquery() used = db.session.query(psq.c.id, Ipblock.address, Ipblock.prefix)\ .outerjoin(Ipblock, Ipblock.parent_id == psq.c.id)\ .order_by(psq.c.priority, psq.c.id, Ipblock.address).all() result = [] for pid, children in groupby(used, key=lambda x: x[0]): parent = parents[pid] range_start = parent.network.address ranges = [] for _, address, prefix in children: if address is None: continue address = int(address) # Process the range before child range_end = address - 1 if range_start <= range_end: ranges.append([range_start, range_end]) # Prepare for the next range range_start = address + 2**(parent.bits - prefix) # Process the range after the last child range_end = parent.broadcast.address if range_start <= range_end: if range_start == parent.network.address: # We need to split the range if we had no children, otherwise we # could try to allocate a child with the same prefix, which is not # possible mid = ((range_end - range_start + 1) % 2 + range_start) ranges.append([range_start, mid - 1]) ranges.append([mid, range_end]) else: ranges.append([range_start, range_end]) result.append(ranges) return result
def substract_random(): # Collect candidate ranges candidates = [] numaddrs = 2**(total_bits - prefix) for i in range(len(ranges)): range_start, range_end = ranges[i] start = align(range_start, numaddrs) count = (range_end - start + 1) // numaddrs if count > 0: candidates.append(CandidateRange(start, range_end, count, i)) # Get a random sample of at most maxnr block positions nr_candidates = sum(c.count for c in candidates) if nr_candidates == 0: return [] if maxnr >= nr_candidates: positions = list(range(nr_candidates)) else: positions = random_sample(nr_candidates, maxnr) # Return IP objects for the selected positions cid = 0 # current CandidateRange id sofar = 0 # number of blocks until cid result = [] to_remove = {} for i in sorted(positions): # Advance cid until we get to block i while i - sofar >= candidates[cid].count: sofar += candidates[cid].count cid += 1 assert cid < len(candidates) block = IP(candidates[cid].start + (i - sofar) * numaddrs, prefix, version) result.append(block) to_remove.setdefault(candidates[cid].range, []).append(block) for i, blocks in list(to_remove.items()): remove_blocks(ranges, i, blocks) return result
def test_list_ips_after(self): self.r.ipblock_create('13.0.0.0/8', status='Container') self.r.ippool_create('pool1', vlan=5) self.r.ippool_add_subnet('pool1', '13.0.4.0/22') self.r.ippool_add_subnet('pool1', '13.0.0.0/22') subnet = IP('13.0.0.0') def nth(n): return str(IP(subnet.address + n, prefix=32, version=4)) def iprange(start, end): return [nth(i) for i in range(start, end + 1)] assert ips(self.r.ip_list(pool='pool1', type='all', limit=5)) == iprange(0, 4) assert ips( self.r.ip_list(pool='pool1', type='all', after='13.0.0.4', limit=5)) == iprange(5, 9) assert ips( self.r.ip_list(pool='*', type='all', after='13.0.3.207', limit=49)) == iprange(3 * 256 + 207 + 1, 3 * 256 + 207 + 49) assert ips(self.r.ip_list(pool='*', type='all', offset=3 * 256 + 207 + 1, limit=49))\ == iprange(3 * 256 + 207 + 1, 3 * 256 + 207 + 49)
def _row_info(row, incl): def action(row): def context(prep='in'): c = [] if incl is not None: for attr in incl: attr_value = getattr(row, attr, None) if attr_value is not None: c.append(attr + ' ' + attr_value) if len(c) > 0: return (' %s ' % prep) + ' '.join(c) return '' use_context_for_attrs = row.objclass in ('rr', 'zone', 'ipblock', 'zone-view') if row.action == 'renamed': return 'renamed%s' % (' to ' + row.newvalue if row.newvalue else '') elif row.action == 'set_attr': return '%s %s=%s%s' % (row.action, row.attrname, row.newvalue, context() if use_context_for_attrs else '') elif row.action == 'del_attr': return '%s %s%s' % (row.action, row.attrname, context() if use_context_for_attrs else '') # DNS elif row.action in [ 'created', 'deleted' ] and row.objclass in ('rr', 'ippool', 'ipblock', 'zone-view'): return '%s%s' % (row.action, context()) elif row.action in ['added', 'removed' ] and row.objclass == 'zone-group': return '%s zone %s view %s' % (row.action, row.zone, row.view) elif row.action in ['added', 'removed' ] and row.objclass == 'output': return '%s zone-group %s' % (row.action, row.zonegroup) elif row.action in ['add zone', 'remove zone' ] and row.objclass == 'registrar-account': return '%s %s' % (row.action, row.zone) elif row.action in ['published' ] and row.objclass == 'registrar-account': return row.action_info # Groups elif row.objclass == 'group' and row.action in [ 'added', 'removed' ]: if row.username is not None: return '%s user %s' % (row.action, row.username) else: return '%s department_number %s %s' % ( row.action, row.department_number) elif row.objclass == 'group' and row.action in [ 'granted', 'revoked' ]: return '%s %s%s' % (row.action, row.right, ('' if row.object is None else ' on ' + row.object)) elif row.objclass == 'group' and row.action == 'department_number': return 'set department_number to %s' % row.department_number elif row.objclass == 'groupright': return '%s %s%s' % (row.action, row.right, ('' if row.object is None else ' on ' + row.object)) # IP elif row.objclass == 'ippool' and ( row.action.startswith('create ') or row.action.startswith('delete ')): return '%s %s' % (row.action, format_ip(row.address, row.version, row.prefix)) else: return row.action def name(row): if row.objclass == 'rr': return '%s %s %s' % (row.name, row.type, row.value) elif row.objclass == 'ipblock': return IP(int(row.address), row.prefix, row.version).label() elif row.objclass == 'groupright': return row.group elif row.objclass == 'key': return row.label else: return row.name def objclass(name): return name if name != 'groupright' else 'group' try: originating_ip = IP(int(row.call_ip_address), version=row.call_ip_version).label() except: originating_ip = None return { 'timestamp': row.timestamp, 'user': row.user, 'tool': row.tool if row.tool is not None else 'native', 'originating_ip': originating_ip, 'name': name(row), 'objclass': objclass(row.objclass), 'action': action(row) }
def format_ip(value, version, prefix=None): return IP(int(value), version=version, prefix=prefix).label() if value else None
def gateway_ip(self): return IP(int(self.gateway), version=self.version)
def ip(self): return IP(int(self.address), self.prefix, self.version)
def query_ip(ip_str): layer3domain = Layer3Domain.query.first() return Ipblock.query_ip(IP(ip_str), layer3domain)
def test_mask(): ip1 = IP('12.0.0.0/24') assert ip1.hostmask == 0x000000ff assert ip1.netmask == 0xffffff00
def nth(n): return str(IP(subnet.address + n, prefix=32, version=4))
def ipstr(nr, version=4): # debugging helper return str(IP(nr, version=version))
def iprange(start, end): return [ str(IP(IP('13.0.0.0').address + i, prefix=32, version=4)) for i in range(start, end + 1) ]