def test_address_not_in_cidr(self): self.network_db = self.insert_network() self.subnet_v4_db = self.insert_subnet( self.network_db, "192.168.0.0/24") self.ip_address_v4 = netaddr.IPAddress("192.168.1.1") ip_address_db = self.insert_ip_address(self.ip_address_v4, self.network_db, self.subnet_v4_db) self.transaction = self.insert_transaction() ip_kwargs = { "network_id": self.network_db["id"], "reuse_after": self.REUSE_AFTER, "deallocated": True, "version": 4, } reallocated = db_api.ip_address_reallocate( self.context, {"transaction_id": self.transaction.id}, **ip_kwargs) self.assertTrue(reallocated) updated_address = db_api.ip_address_reallocate_find( self.context, self.transaction.id) self.assertIsNone(updated_address) self.context.session.flush() self.assertIsNone(db_api.ip_address_find(self.context, id=ip_address_db.id, scope=db_api.ONE))
def test_subnet_do_not_use(self): self.network_db = self.insert_network() self.subnet_v4_db = self.insert_subnet( self.network_db, "192.168.0.0/24", do_not_use=True) self.ip_address_v4 = netaddr.IPAddress("192.168.0.1") self.insert_ip_address(self.ip_address_v4, self.network_db, self.subnet_v4_db) self.transaction = self.insert_transaction() ip_kwargs = { "network_id": self.network_db["id"], "reuse_after": self.REUSE_AFTER, "deallocated": True, "ip_address": None, "version": 4, "subnet_id": None } reallocated = db_api.ip_address_reallocate( self.context, {"transaction_id": self.transaction.id}, **ip_kwargs) self.assertTrue(reallocated) updated_address = db_api.ip_address_reallocate_find( self.context, self.transaction.id) self.assertIsNone(updated_address)
def test_normal_one_of_multiple_potential_ip_addresses(self): ip_kwargs = { "network_id": self.network_db["id"], "reuse_after": self.REUSE_AFTER, "deallocated": True, } reallocated = db_api.ip_address_reallocate( self.context, {"transaction_id": self.transaction.id}, **ip_kwargs) self.assertTrue(reallocated)
def test_normal_v6(self): ip_kwargs = { "network_id": self.network_db["id"], "reuse_after": self.REUSE_AFTER, "deallocated": True, "version": 6, } reallocated = db_api.ip_address_reallocate( self.context, {"transaction_id": self.transaction.id}, **ip_kwargs) self.assertTrue(reallocated)
def test_reuse_after_not_time_yet(self): ip_kwargs = { "network_id": self.network_db["id"], "reuse_after": self.REUSE_AFTER * 2, "deallocated": True, "version": 4, } reallocated = db_api.ip_address_reallocate( self.context, {"transaction_id": self.transaction.id}, **ip_kwargs) self.assertFalse(reallocated)
def test_ip_address_specified_deallocated_None(self): ip_kwargs = { "network_id": self.network_db["id"], "reuse_after": self.REUSE_AFTER, "ip_address": self.ip_address_v4, "version": 4, } reallocated = db_api.ip_address_reallocate( self.context, {"transaction_id": self.transaction.id}, **ip_kwargs) self.assertTrue(reallocated)
def test_subnet_ids_specified(self): ip_kwargs = { "network_id": self.network_db["id"], "reuse_after": self.REUSE_AFTER, "deallocated": True, "version": 4, "subnet_id": [self.subnet_v4_db["id"]] } reallocated = db_api.ip_address_reallocate( self.context, {"transaction_id": self.transaction.id}, **ip_kwargs) self.assertTrue(reallocated)
def test_normal_v6(self): self.default_case() ip_kwargs = { "network_id": self.network_db["id"], "reuse_after": self.REUSE_AFTER, "deallocated": True, "version": 6, } reallocated = db_api.ip_address_reallocate( self.context, {"transaction_id": self.transaction.id}, **ip_kwargs) self.assertTrue(reallocated) updated_address = db_api.ip_address_reallocate_find( self.context, self.transaction.id) self.assertEqual(updated_address["address"], int(self.ip_address_v6.ipv6()))
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after, version=None, ip_address=None, segment_id=None, subnets=None, **kwargs): version = version or [4, 6] elevated = context.elevated() LOG.info("Attempting to reallocate an IP (step 1 of 3) - [{0}]".format( utils.pretty_kwargs(network_id=net_id, port_id=port_id, version=version, segment_id=segment_id, subnets=subnets, ip_address=ip_address))) if version == 6: # Defers to the create case. The reason why is we'd have to look # up subnets here to correctly generate the v6. If we split them # up into reallocate and create, we'd be looking up the same # subnets twice, which is a waste of time. # TODO(mdietz): after reviewing this code, this block annoyingly # doesn't trigger in the ANY case, since we end up # using a list of [4, 6]. It works as expected most # of the time, but we can anticipate that isolated # networks will end up using sequential assignment. # Probably want to rework this logic to compensate # at some point. Considering they all come from the # same MAC address pool, nothing bad will happen, # just worth noticing and fixing. LOG.info("Identified as v6 case, deferring to IP create path") return [] sub_ids = [] if subnets: sub_ids = subnets elif segment_id: subnets = db_api.subnet_find(elevated, network_id=net_id, segment_id=segment_id) sub_ids = [s["id"] for s in subnets] if not sub_ids: LOG.info("No subnets matching segment_id {0} could be " "found".format(segment_id)) raise ip_address_failure(net_id) ip_kwargs = { "network_id": net_id, "deallocated": True, "version": version, "lock_id": None, } if reuse_after is not None: ip_kwargs["reuse_after"] = reuse_after if ip_address is not None: ip_kwargs["ip_address"] = ip_address del ip_kwargs["deallocated"] if sub_ids: ip_kwargs["subnet_id"] = sub_ids ipam_log = kwargs.get('ipam_log', None) for retry in xrange(CONF.QUARK.ip_address_retry_max): attempt = None if ipam_log: attempt = ipam_log.make_entry("attempt_to_reallocate_ip") LOG.info("Attempt {0} of {1}".format( retry + 1, CONF.QUARK.ip_address_retry_max)) try: with context.session.begin(): transaction = db_api.transaction_create(context) m = models.IPAddress update_kwargs = { m.transaction_id: transaction.id, m.address_type: kwargs.get("address_type", ip_types.FIXED), m.deallocated: False, m.deallocated_at: None, m.used_by_tenant_id: context.tenant_id, m.allocated_at: timeutils.utcnow(), } result = db_api.ip_address_reallocate( elevated, update_kwargs, **ip_kwargs) if not result: LOG.info("Couldn't update any reallocatable addresses " "given the criteria") if attempt: attempt.failed() break updated_address = db_api.ip_address_reallocate_find( elevated, transaction.id) if not updated_address: if attempt: attempt.failed() continue LOG.info("Address {0} is reallocated".format( updated_address["address_readable"])) return [updated_address] except Exception: if attempt: attempt.failed() LOG.exception("Error in reallocate ip...") finally: if attempt: attempt.end() return []
def attempt_to_reallocate_ip(self, context, net_id, port_id, reuse_after, version=None, ip_address=None, segment_id=None, subnets=None, **kwargs): version = version or [4, 6] elevated = context.elevated() LOG.info("Attempting to reallocate an IP (step 1 of 3) - [{0}]".format( utils.pretty_kwargs(network_id=net_id, port_id=port_id, version=version, segment_id=segment_id, subnets=subnets))) if version == 6: # Defers to the create case. The reason why is we'd have to look # up subnets here to correctly generate the v6. If we split them # up into reallocate and create, we'd be looking up the same # subnets twice, which is a waste of time. # TODO(mdietz): after reviewing this code, this block annoyingly # doesn't trigger in the ANY case, since we end up # using a list of [4, 6]. It works as expected most # of the time, but we can anticipate that isolated # networks will end up using sequential assignment. # Probably want to rework this logic to compensate # at some point. Considering they all come from the # same MAC address pool, nothing bad will happen, # just worth noticing and fixing. LOG.info("Identified as v6 case, deferring to IP create path") return [] sub_ids = [] if subnets: sub_ids = subnets elif segment_id: subnets = db_api.subnet_find(elevated, network_id=net_id, segment_id=segment_id) sub_ids = [s["id"] for s in subnets] if not sub_ids: LOG.info("No subnets matching segment_id {0} could be " "found".format(segment_id)) raise exceptions.IpAddressGenerationFailure( net_id=net_id) ip_kwargs = { "network_id": net_id, "reuse_after": reuse_after, "deallocated": True, "ip_address": ip_address, "version": version, } if ip_address: del ip_kwargs["deallocated"] if sub_ids: ip_kwargs["subnet_id"] = sub_ids ipam_log = kwargs.get('ipam_log', None) for retry in xrange(CONF.QUARK.ip_address_retry_max): attempt = None if ipam_log: attempt = ipam_log.make_entry("attempt_to_reallocate_ip") LOG.info("Attempt {0} of {1}".format( retry + 1, CONF.QUARK.ip_address_retry_max)) try: with context.session.begin(): transaction = db_api.transaction_create(context) m = models.IPAddress update_kwargs = { m.transaction_id: transaction.id, m.address_type: kwargs.get("address_type", ip_types.FIXED), m.deallocated: False, m.deallocated_at: None, m.used_by_tenant_id: context.tenant_id, m.allocated_at: timeutils.utcnow(), } result = db_api.ip_address_reallocate( elevated, update_kwargs, **ip_kwargs) if not result: LOG.info("Couldn't update any reallocatable addresses " "given the criteria") if attempt: attempt.failed() break updated_address = db_api.ip_address_reallocate_find( elevated, transaction.id) if not updated_address: if attempt: attempt.failed() continue LOG.info("Address {0} is reallocated".format( updated_address["address_readable"])) return [updated_address] except Exception: if attempt: attempt.failed() LOG.exception("Error in reallocate ip...") finally: if attempt: attempt.end() return []