def free_topology(tid): """Deletes the topology associated with tid, as well as any IPAssignment, MACAssignment, TopologySourceIPFilter, TopologyUserFilter, and IPBlockAllocation objects belonging to it.""" def run(): try: topo = db.Topology.objects.get(pk=tid) db.TopologySourceIPFilter.objects.filter(topology=topo).delete() db.TopologyUserFilter.objects.filter(topology=topo).delete() db.IPAssignment.objects.filter(topology=topo).delete() db.MACAssignment.objects.filter(topology=topo).delete() db.IPBlockAllocation.objects.filter(topology=topo).delete() topo.delete() logging.info('freed topology %d' % tid) except db.Topology.DoesNotExist: logging.warning('asked to free non-existent topology %d' % tid) DBService.run_background(run)
def __realloc_if_available_work(ra, ip_block_from): # Start a database query for the IP block allocations which we'll need later closest_pre_alloc_wait = DBService.run_background(lambda:db.IPBlockAllocation.objects.filter(start_addr__lte=ra.start_addr).order_by('-start_addr')[0]) print closest_pre_alloc_wait # the recent allocation must be from the block we're trying to allocate from start_addr = struct.unpack('>I', inet_aton(ra.start_addr))[0] start_addr_from = struct.unpack('>I', inet_aton(ip_block_from.subnet))[0] if not is_overlapping(start_addr, ra.mask, start_addr_from, ip_block_from.mask): return None # the recent allocation must not be in use try: # does the closest active allocation BEFORE the recent alloc overlap it? closest_pre_alloc = closest_pre_alloc_wait() sa_pre = struct.unpack('>I', inet_aton(closest_pre_alloc.start_addr))[0] if is_overlapping(start_addr, ra.mask, sa_pre, closest_pre_alloc.mask): return None # does the closest active allocation AFTER to the recent alloc overlap it? closest_post_alloc = DBService.run_and_wait(lambda:db.IPBlockAllocation.objects.filter(start_addr__gte=ra.start_addr).order_by('start_addr')[0]) sa_post = struct.unpack('>I', inet_aton(closest_post_alloc.start_addr))[0] if is_overlapping(start_addr, ra.mask, sa_post, closest_post_alloc.mask): return None except IndexError: pass # it isn't in use => allocate it new_alloc = db.IPBlockAllocation() new_alloc.block_from = ip_block_from new_alloc.topology = None new_alloc.start_addr = ra.start_addr new_alloc.mask = ra.mask DBService.run_and_wait(new_alloc.save) logging.info('RE-allocated new block of addresses: %s' % new_alloc) return new_alloc
def allocate_to_topology(topo, ip_block_from, owner, use_first_available=False, use_recent_alloc_logic=True): """Allocates IP addresses to an existing topology. If the topology already has IPs assigned, this does nothing. @param topo The Topology to allocate IPs to @param ip_block_from The IPBlock from which to allocate addresses @param use_first_available If True, use the first available IP addresses rather than a random block @param owner The user who has caused this allocation to take place @exception IPError If there are not enough IPs to allocate""" # Check we don't have any IPs already try: _ = DBService.run_and_wait(lambda:db.IPBlockAllocation.objects.get(topology=topo)) except db.IPBlockAllocation.DoesNotExist: pass else: # We already have an IP block allocation - do nothing return # Get the template this topology was created from, and the topology's source # filters template = topo.template filters = DBService.run_and_wait(lambda:db.TopologySourceIPFilter.objects.filter(topology=topo)) src_filters = [(sif.ip, sif.mask) for sif in filters] # build a depth-first "tree" of the topology from the port connected to the gateway root = template.get_root_port() if not root: return ("template '%s' has no ports" % template.name,) try: tree = root.get_tree(True) num_addrs = tree.compute_subnet_size() except: # topology graph has cycles - just make each link its own /31 tree = None links = DBService.run_and_wait(lambda:db.Link.objects.filter(port1__node__template=template)) num_addrs = len(links) * 2 # try to give the user the allocation they most recently had alloc = __realloc_if_available(owner, template, ip_block_from) if use_recent_alloc_logic else None if not alloc: # allocate a subblock of IPs for the new topology allocs = allocate_ip_block(ip_block_from, 1, num_addrs, src_filters, use_first_available) if not allocs: raise IPError("Not enough IP addresses to allocate for topology %d" % topo.id) alloc = allocs[0] # Assign IP addresses start_addr = struct.unpack('>I', inet_aton(alloc.start_addr))[0] if tree: assignments = tree.assign_addr(start_addr, alloc.size()) else: assignments = [] for i,link in enumerate(links): assignments.append((link.port1, start_addr+2*i, 31)) assignments.append((link.port2, start_addr+2*i+1, 31)) # Add the allocation to the database alloc.topology = topo alloc.save() # Add the allocation to the list of recently allocated blocks if use_recent_alloc_logic: ra = db.RecentIPBlockAllocation() ra.user = owner ra.template = template ra.start_addr = alloc.start_addr ra.mask = alloc.mask # Save and wait for it - we wait because we need the ID for the next bit DBService.run_and_wait(ra.save) # Add the assignments to the database wait_for = [] for port, ip, mask_sz in assignments: ipa = db.IPAssignment() ipa.topology = topo ipa.port = port ipa.ip = inet_ntoa(struct.pack('>I', ip)) ipa.mask = mask_sz waiter = DBService.run_background(ipa.save) wait_for.append(waiter) logging.info('IP assignment for topology %d: %s' % (topo.id, ipa)) # Wait for all the DB transactions to complete for waiter in wait_for: waiter()