From 84f4c254b4cc0b0bb3307ec742edf9555c6fd475 Mon Sep 17 00:00:00 2001
From: Jacek Kowalski <Jacek@jacekk.info>
Date: Wed, 11 Mar 2026 19:24:05 +0000
Subject: [PATCH] Add validate=0 option to allow duplicate address assignments

---
 lib/Ipam.py          |    3 ++-
 test/IpamPoolTest.py |   16 ++++++++++++++++
 README.md            |    9 +++++++++
 3 files changed, 27 insertions(+), 1 deletions(-)

diff --git a/README.md b/README.md
index c4360c0..58324ce 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,15 @@
 In this mode all IP addresses are handed out from the subnet,
 including ones that would be "network address" and "broadcast address"!
 
+`validate=0`
+
+Do not validate duplicate IP address assignment. This IPAM plugin would
+then happily hand out already-used addresses if such were manually specified.
+This option does not affect automatic assignments.
+Note that this module does not track how many times the IP was handed out,
+hence if two containers have the same IP and one of them stops,
+IP will be marked as free!
+
 ## Manual packaging
 
 In order to test this module in development environment, you can build it
diff --git a/lib/Ipam.py b/lib/Ipam.py
index 2cffed2..f1d7659 100644
--- a/lib/Ipam.py
+++ b/lib/Ipam.py
@@ -41,6 +41,7 @@
         if not self.subpool.subnet_of(self.pool):
             raise InputValidationException('Subpool must be a subnet of pool')
 
+        self.validate = self.options.get('validate', '1') == '1'
         self.ptp = self.options.get('ptp', '0') == '1'
 
         self.allocations = set()
@@ -84,7 +85,7 @@
             raise InputValidationException('Requested address does not belong to a pool')
 
         address = str(address)
-        if self._is_allocated(address):
+        if self.validate and self._is_allocated(address):
             raise InputValidationException('Requested address {} is already used'.format(address))
         self.allocations.add(address)
 
diff --git a/test/IpamPoolTest.py b/test/IpamPoolTest.py
index 2dda0a5..04a714b 100644
--- a/test/IpamPoolTest.py
+++ b/test/IpamPoolTest.py
@@ -310,3 +310,19 @@
         self.assertEqual(pool.allocate(), 'fe80::1/128')
         self.assertEqual(pool.allocate(), 'fe80::2/128')
         self.assertEqual(pool.allocate(), 'fe80::3/128')
+
+class TestPoolWithoutValidation(unittest.TestCase):
+    def test_pool_allocate_duplicates_ipv4(self):
+        pool = Pool(pool='127.0.0.0/30', options={'validate': '0'})
+        self.assertEqual(pool.allocate(), '127.0.0.1/30')
+        self.assertEqual(pool.allocate(), '127.0.0.2/30')
+        self.assertEqual(pool.allocate('127.0.0.1'), '127.0.0.1/30')
+        self.assertEqual(pool.allocate('127.0.0.1'), '127.0.0.1/30')
+
+    def test_pool_allocate_duplicates_ipv6(self):
+        pool = Pool(pool='fe80::/126', options={'validate': '0'})
+        self.assertEqual(pool.allocate(), 'fe80::1/126')
+        self.assertEqual(pool.allocate(), 'fe80::2/126')
+        self.assertEqual(pool.allocate(), 'fe80::3/126')
+        self.assertEqual(pool.allocate('fe80::1'), 'fe80::1/126')
+        self.assertEqual(pool.allocate('fe80::1'), 'fe80::1/126')

--
Gitblit v1.10.0