def _populate_segment_allocation_range(self, sa_range): """Populate a given segment range.""" # Range of ids to allocate, first to last (inclusive) id_range = xrange(sa_range["first_id"], sa_range["last_id"] + 1) sa_dicts = [] total = 0 for i in id_range: sa_dicts.append( { "segment_id": sa_range["segment_id"], "segment_type": sa_range["segment_type"], "id": i, "segment_allocation_range_id": sa_range["id"], "deallocated": True, } ) total = total + 1 db_api.segment_allocation_range_populate_bulk(self.context, sa_dicts) self.context.session.flush() # assert our allocation were actually created allocs = db_api.segment_allocation_find(self.context, segment_allocation_range_id=sa_range["id"]).all() self.assertEqual(len(allocs), len(id_range))
def test_create_segment_allocation_range_creates_allocations(self): """Assert created segments populate the allocation table.""" sa_range_dict = self._make_segment_allocation_range_dict() sa_range_request = {"segment_allocation_range": sa_range_dict} sa_range = sa_ranges_api.create_segment_allocation_range(self.context, sa_range_request) allocs = db_api.segment_allocation_find(self.context, segment_allocation_range_id=sa_range["id"]).all() self.assertEqual(len(allocs), sa_range["size"])
def test_create_segment_allocation_range_creates_allocations(self): """Assert created segments populate the allocation table.""" sa_range_dict = self._make_segment_allocation_range_dict() sa_range_request = {"segment_allocation_range": sa_range_dict} sa_range = sa_ranges_api.create_segment_allocation_range( self.context, sa_range_request) allocs = db_api.segment_allocation_find( self.context, segment_allocation_range_id=sa_range['id']).all() self.assertEqual(len(allocs), sa_range['size'])
def _delete_segment_allocation_range(context, sa_range): allocs = db_api.segment_allocation_find( context, segment_allocation_range_id=sa_range["id"], deallocated=False).count() if allocs: raise q_exc.SegmentAllocationRangeInUse( segment_allocation_range_id=sa_range["id"]) db_api.segment_allocation_range_delete(context, sa_range)
def test_delete_segment_allocation_range_deletes(self): sa_range = self._create_segment_allocation_range() sa_range_id = sa_range["id"] sa_ranges_api.delete_segment_allocation_range(self.context, sa_range_id) # assert that the range and it's unused allocations are deleted sa_range = db_api.segment_allocation_range_find(self.context, id=sa_range_id, scope=db_api.ALL) allocs = db_api.segment_allocation_find(self.context, segment_allocation_range_id=sa_range_id).all() self.assertEqual(sa_range, []) self.assertEqual(allocs, [])
def _delete_segment_allocation_range(context, sa_range): allocs = db_api.segment_allocation_find( context, segment_allocation_range_id=sa_range["id"], deallocated=False).count() if allocs: raise quark_exceptions.SegmentAllocationRangeInUse( segment_allocation_range_id=sa_range["id"]) db_api.segment_allocation_range_delete(context, sa_range)
def _allocate_segment(self, sa_range, count=1): """Populate a given segment range.""" allocs = [] for i in xrange(sa_range["first_id"], sa_range["first_id"] + count): filters = {"segment_allocation_range_id": sa_range["id"], "deallocated": True} alloc = db_api.segment_allocation_find(self.context, **filters).first() if not alloc: raise Exception("Could not find deallocated id.") update = {"deallocated": False} allocs.append(db_api.segment_allocation_update(self.context, alloc, **update)) self.context.session.flush() self.assertEqual(len(allocs), count) return allocs
def test_segment_deallocation(self): # We call the allocate test to set up an initial allocation # and assert that it actually worked. sa_range, alloc = self.test_segment_allocation() self.driver.deallocate(self.context, sa_range["segment_id"], "network_id_1") # assert that our previous allocation is now free allocs = db_api.segment_allocation_find(self.context, id=alloc["id"], segment_id=sa_range["segment_id"]).all() self.assertEqual(len(allocs), 1) self.assertTrue(allocs[0]["deallocated"]) self.assertEqual(allocs[0]["network_id"], None)
def test_delete_segment_allocation_range_deletes(self): sa_range = self._create_segment_allocation_range() sa_range_id = sa_range["id"] sa_ranges_api.delete_segment_allocation_range(self.context, sa_range_id) # assert that the range and it's unused allocations are deleted sa_range = db_api.segment_allocation_range_find(self.context, id=sa_range_id, scope=db_api.ALL) allocs = db_api.segment_allocation_find( self.context, segment_allocation_range_id=sa_range_id).all() self.assertEqual(sa_range, []) self.assertEqual(allocs, [])
def test_segment_allocation(self): sa_range = self._create_segment_allocation_range() # assert we allocate and update correctly alloc = self.driver.allocate(self.context, sa_range["segment_id"], "network_id_1") self.assertEqual(alloc["segment_type"], sa_range["segment_type"]) self.assertEqual(alloc["segment_id"], sa_range["segment_id"]) self.assertEqual(alloc["network_id"], "network_id_1") # assert the remaining allocations remain unallocated allocs = db_api.segment_allocation_find(self.context).all() allocs.remove(alloc) self.assertEqual(len(allocs), 4) self.assertTrue(all([a["deallocated"] for a in allocs])) return sa_range, alloc
def test_segment_deallocation(self): # We call the allocate test to set up an initial allocation # and assert that it actually worked. sa_range, alloc = self.test_segment_allocation() self.driver.deallocate(self.context, sa_range['segment_id'], 'network_id_1') # assert that our previous allocation is now free allocs = db_api.segment_allocation_find( self.context, id=alloc['id'], segment_id=sa_range['segment_id']).all() self.assertEqual(len(allocs), 1) self.assertTrue(allocs[0]["deallocated"]) self.assertEqual(allocs[0]["network_id"], None)
def test_segment_allocation(self): sa_range = self._create_segment_allocation_range() # assert we allocate and update correctly alloc = self.driver.allocate(self.context, sa_range['segment_id'], 'network_id_1') self.assertEqual(alloc['segment_type'], sa_range['segment_type']) self.assertEqual(alloc['segment_id'], sa_range['segment_id']) self.assertEqual(alloc['network_id'], 'network_id_1') # assert the remaining allocations remain unallocated allocs = db_api.segment_allocation_find(self.context).all() allocs.remove(alloc) self.assertEqual(len(allocs), 4) self.assertTrue(all([a["deallocated"] for a in allocs])) return sa_range, alloc
def _try_deallocate(self, context, segment_id, network_id): LOG.info("Attempting to deallocate segment for network %s " "segment_id %s segment_type %s" % (network_id, segment_id, self.segment_type)) with context.session.begin(subtransactions=True): filter_dict = { "deallocated": False, "segment_id": segment_id, "segment_type": self.segment_type, "network_id": network_id } allocations = db_api.segment_allocation_find( context, **filter_dict).all() if not allocations: LOG.info("Could not find allocated segment for network %s " "segment_id %s segment_type %s for deallocate." % (network_id, segment_id, self.segment_type)) return if len(allocations) > 1: LOG.error("Found multiple allocated segments for network %s " "segment_id %s segment_type %s for deallocate. " "Refusing to deallocate, these allocations are now " "orphaned." % (network_id, segment_id, self.segment_type)) return allocation = allocations[0] # Deallocate the found segment. update_dict = { "deallocated": True, "deallocated_at": timeutils.utcnow(), "network_id": None } allocation = db_api.segment_allocation_update( context, allocation, **update_dict) LOG.info("Deallocated %s allocated segment(s) for network %s " "segment_id %s segment_type %s" % (len(allocations), network_id, segment_id, self.segment_type))
def _allocate_segment(self, sa_range, count=1): """Populate a given segment range.""" allocs = [] for i in xrange(sa_range['first_id'], sa_range['first_id'] + count): filters = { 'segment_allocation_range_id': sa_range['id'], 'deallocated': True } alloc = db_api.segment_allocation_find(self.context, **filters).first() if not alloc: raise Exception("Could not find deallocated id.") update = {'deallocated': False} allocs.append( db_api.segment_allocation_update(self.context, alloc, **update)) self.context.session.flush() self.assertEqual(len(allocs), count) return allocs
def get_segment_allocation_range(context, id, fields=None): LOG.info("get_segment_allocation_range %s for tenant %s fields %s" % (id, context.tenant_id, fields)) if not context.is_admin: raise n_exc.NotAuthorized() sa_range = db_api.segment_allocation_range_find(context, id=id, scope=db_api.ONE) if not sa_range: raise q_exc.SegmentAllocationRangeNotFound( segment_allocation_range_id=id) # Count up allocations so we can calculate how many are free. allocs = db_api.segment_allocation_find( context, segment_allocation_range_id=sa_range["id"], deallocated=False).count() return v._make_segment_allocation_range_dict(sa_range, allocations=allocs)
def get_segment_allocation_range(context, id, fields=None): LOG.info("get_segment_allocation_range %s for tenant %s fields %s" % (id, context.tenant_id, fields)) if not context.is_admin: raise exceptions.NotAuthorized() sa_range = db_api.segment_allocation_range_find( context, id=id, scope=db_api.ONE) if not sa_range: raise quark_exceptions.SegmentAllocationRangeNotFound( segment_allocation_range_id=id) # Count up allocations so we can calculate how many are free. allocs = db_api.segment_allocation_find( context, segment_allocation_range_id=sa_range["id"], deallocated=False).count() return v._make_segment_allocation_range_dict( sa_range, allocations=allocs)
def _populate_segment_allocation_range(self, sa_range): """Populate a given segment range.""" # Range of ids to allocate, first to last (inclusive) id_range = xrange(sa_range['first_id'], sa_range['last_id'] + 1) sa_dicts = [] total = 0 for i in id_range: sa_dicts.append({ 'segment_id': sa_range['segment_id'], 'segment_type': sa_range['segment_type'], 'id': i, 'segment_allocation_range_id': sa_range['id'], 'deallocated': True }) total = total + 1 db_api.segment_allocation_range_populate_bulk(self.context, sa_dicts) self.context.session.flush() # assert our allocation were actually created allocs = db_api.segment_allocation_find( self.context, segment_allocation_range_id=sa_range['id']).all() self.assertEqual(len(allocs), len(id_range))
def _try_allocate(self, context, segment_id, network_id): """Find a deallocated network segment id and reallocate it. NOTE(morgabra) This locks the segment table, but only the rows in use by the segment, which is pretty handy if we ever have more than 1 segment or segment type. """ LOG.info("Attempting to allocate segment for network %s " "segment_id %s segment_type %s" % (network_id, segment_id, self.segment_type)) filter_dict = { "segment_id": segment_id, "segment_type": self.segment_type, "do_not_use": False } available_ranges = db_api.segment_allocation_range_find( context, scope=db_api.ALL, **filter_dict) available_range_ids = [r["id"] for r in available_ranges] try: with context.session.begin(subtransactions=True): # Search for any deallocated segment ids for the # given segment. filter_dict = { "deallocated": True, "segment_id": segment_id, "segment_type": self.segment_type, "segment_allocation_range_ids": available_range_ids } # NOTE(morgabra) We select 100 deallocated segment ids from # the table here, and then choose 1 randomly. This is to help # alleviate the case where an uncaught exception might leave # an allocation active on a remote service but we do not have # a record of it locally. If we *do* end up choosing a # conflicted id, the caller should simply allocate another one # and mark them all as reserved. If a single object has # multiple reservations on the same segment, they will not be # deallocated, and the operator must resolve the conficts # manually. allocations = db_api.segment_allocation_find( context, lock_mode=True, **filter_dict).limit(100).all() if allocations: allocation = random.choice(allocations) # Allocate the chosen segment. update_dict = { "deallocated": False, "deallocated_at": None, "network_id": network_id } allocation = db_api.segment_allocation_update( context, allocation, **update_dict) LOG.info("Allocated segment %s for network %s " "segment_id %s segment_type %s" % (allocation["id"], network_id, segment_id, self.segment_type)) return allocation except Exception: LOG.exception("Error in segment reallocation.") LOG.info("Cannot find reallocatable segment for network %s " "segment_id %s segment_type %s" % (network_id, segment_id, self.segment_type))