diff --git a/etc/melange/melange.conf.sample b/etc/melange/melange.conf.sample index 095efe4c..3d194301 100644 --- a/etc/melange/melange.conf.sample +++ b/etc/melange/melange.conf.sample @@ -44,6 +44,11 @@ api_extensions_path = melange/extensions dns1 = 8.8.8.8 dns2 = 8.8.4.4 +#Release Deallocated fixed IPs immediately +#If set to True, deallocated IPs would be deleted only when +#bin/melange-delete-deallocated-ips script is executed +keep_deallocated_ips = False + #Number of days before deallocated IPs are deleted keep_deallocated_ips_for_days = 2 diff --git a/melange/ipam/models.py b/melange/ipam/models.py index 735fb66e..2330a21d 100644 --- a/melange/ipam/models.py +++ b/melange/ipam/models.py @@ -233,6 +233,11 @@ class IpAddressIterator(object): raise StopIteration +def deallocated_by_date(): + days = config.Config.get('keep_deallocated_ips_for_days', 2) + return utils.utcnow() - datetime.timedelta(days=int(days)) + + class IpBlock(ModelBase): PUBLIC_TYPE = "public" @@ -256,7 +261,8 @@ class IpBlock(ModelBase): def delete_all_deallocated_ips(cls): LOG.info("Deleting all deallocated IPs") for block in db.db_api.find_all_blocks_with_deallocated_ips(): - block.delete_deallocated_ips() + block.delete_deallocated_ips( + deallocated_by_func=deallocated_by_date) @property def broadcast(self): @@ -414,20 +420,16 @@ class IpBlock(ModelBase): LOG.debug("Deallocating IP: %s" % ip_address) ip_address.deallocate() - def delete_deallocated_ips(self): + def delete_deallocated_ips(self, deallocated_by_func): self.update(is_full=False) for ip in db.db_api.find_deallocated_ips( - deallocated_by=self._deallocated_by_date(), ip_block_id=self.id): + deallocated_by=deallocated_by_func(), ip_block_id=self.id): LOG.debug("Deleting deallocated IP: %s" % ip) generator = ipv4.plugin().get_generator(self) generator.ip_removed(ip.address) ip.delete() - def _deallocated_by_date(self): - days = config.Config.get('keep_deallocated_ips_for_days', 2) - return utils.utcnow() - datetime.timedelta(days=int(days)) - def subnet(self, cidr, network_id=None, tenant_id=None): network_id = network_id or self.network_id tenant_id = tenant_id or self.tenant_id @@ -1058,6 +1060,11 @@ class Network(ModelBase): ips = IpAddress.find_all_by_network(self.id, interface_id=interface_id) for ip in ips: ip.deallocate() + keep_deallocated_ips = config.Config.get('keep_deallocated_ips', 'False') + if not utils.bool_from_string(keep_deallocated_ips): + LOG.debug("Deleting deallocated ips") + for block in db.db_api.find_all_blocks_with_deallocated_ips(): + block.delete_deallocated_ips(deallocated_by_func=utils.utcnow) def find_allocated_ip(self, **conditions): for ip_block in self.ip_blocks: diff --git a/melange/tests/unit/test_ipam_models.py b/melange/tests/unit/test_ipam_models.py index 168d1939..f65bdf4d 100644 --- a/melange/tests/unit/test_ipam_models.py +++ b/melange/tests/unit/test_ipam_models.py @@ -956,13 +956,30 @@ class TestIpBlock(tests.BaseTest): ip2.deallocate() with unit.StubTime(time=current_time): - models.IpBlock.delete_all_deallocated_ips() + models.IpBlock.delete_all_deallocated_ips( + deallocated_by_func=models.deallocated_by_date) self.assertEqual(models.IpAddress.find_all( ip_block_id=ip_block1.id).all(), []) self.assertEqual(models.IpAddress.find_all( ip_block_id=ip_block2.id).all(), []) + def test_delete_deallocated_ips_immediately(self): + ip_block = factory_models.PrivateIpBlockFactory(cidr="10.0.1.1/24") + current_time = datetime.datetime(2050, 1, 1) + ip1 = _allocate_ip(ip_block) + ip2 = _allocate_ip(ip_block) + ip3 = _allocate_ip(ip_block) + with unit.StubTime(time=current_time): + ip1.deallocate() + ip3.deallocate() + + with unit.StubTime(time=current_time): + ip_block.delete_deallocated_ips(deallocated_by_func=utils.utcnow) + + existing_ips = models.IpAddress.find_all(ip_block_id=ip_block.id).all() + self.assertModelsEqual(existing_ips, [ip2]) + def test_delete_deallocated_ips_after_default_of_two_days(self): ip_block = factory_models.PrivateIpBlockFactory(cidr="10.0.1.1/24") current_time = datetime.datetime(2050, 1, 1) @@ -975,7 +992,8 @@ class TestIpBlock(tests.BaseTest): ip3.deallocate() with unit.StubTime(time=current_time): - ip_block.delete_deallocated_ips() + ip_block.delete_deallocated_ips( + deallocated_by_func=models.deallocated_by_date) existing_ips = models.IpAddress.find_all(ip_block_id=ip_block.id).all() self.assertModelsEqual(existing_ips, [ip2]) @@ -999,7 +1017,8 @@ class TestIpBlock(tests.BaseTest): with unit.StubConfig(keep_deallocated_ips_for_days=1): with unit.StubTime(time=current_time): - ip_block.delete_deallocated_ips() + ip_block.delete_deallocated_ips( + deallocated_by_func=models.deallocated_by_date) self.assertEqual(ip_block.addresses(), [ip2]) @@ -1014,7 +1033,8 @@ class TestIpBlock(tests.BaseTest): interface=interface) self.assertTrue(ip_block.is_full) - models.IpBlock.delete_all_deallocated_ips() + models.IpBlock.delete_all_deallocated_ips( + deallocated_by_func=models.deallocated_by_date) self.assertFalse(models.IpBlock.find(ip_block.id).is_full) @@ -2461,7 +2481,8 @@ class TestAllowedIp(tests.BaseTest): with unit.StubTime(time=two_days_before): block.deallocate_ip(ip.address) with unit.StubTime(time=current_time): - block.delete_deallocated_ips() + block.delete_deallocated_ips( + deallocated_by_func=models.deallocated_by_date) reloaded_ip = models.IpAddress.find(ip.id) self.assertFalse(reloaded_ip.marked_for_deallocation)