Prechádzať zdrojové kódy

libnet/ipamapi: add in/out structs for RequestPool

The `RequestPool` method has many args and named returns. This
makes the code hard to follow at times. This commit adds one struct,
`PoolRequest`, to replace these args, and one struct, `AllocatedPool`,
to replace these named returns.

Both structs' fields are properly documented to better define their
semantics, and their relationship with address allocation.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
Albin Kerouanton 1 rok pred
rodič
commit
9c4c3d7443

+ 10 - 5
libnetwork/cnmallocator/networkallocator.go

@@ -884,13 +884,18 @@ func (na *cnmNetworkAllocator) allocatePools(n *api.Network) (map[string]string,
 	}
 	}
 
 
 	for i, ic := range ipamConfigs {
 	for i, ic := range ipamConfigs {
-		poolID, poolIP, meta, err := ipam.RequestPool(asName, ic.Subnet, ic.Range, dOptions, false)
+		alloc, err := ipam.RequestPool(ipamapi.PoolRequest{
+			AddressSpace: asName,
+			Pool:         ic.Subnet,
+			SubPool:      ic.Range,
+			Options:      dOptions,
+		})
 		if err != nil {
 		if err != nil {
 			// Rollback by releasing all the resources allocated so far.
 			// Rollback by releasing all the resources allocated so far.
 			releasePools(ipam, ipamConfigs[:i], pools)
 			releasePools(ipam, ipamConfigs[:i], pools)
 			return nil, err
 			return nil, err
 		}
 		}
-		pools[poolIP.String()] = poolID
+		pools[alloc.Pool.String()] = alloc.PoolID
 
 
 		// The IPAM contract allows the IPAM driver to autonomously
 		// The IPAM contract allows the IPAM driver to autonomously
 		// provide a network gateway in response to the pool request.
 		// provide a network gateway in response to the pool request.
@@ -902,7 +907,7 @@ func (na *cnmNetworkAllocator) allocatePools(n *api.Network) (map[string]string,
 			gwIP *net.IPNet
 			gwIP *net.IPNet
 			ip   net.IP
 			ip   net.IP
 		)
 		)
-		if gws, ok := meta[netlabel.Gateway]; ok {
+		if gws, ok := alloc.Meta[netlabel.Gateway]; ok {
 			if ip, gwIP, err = net.ParseCIDR(gws); err != nil {
 			if ip, gwIP, err = net.ParseCIDR(gws); err != nil {
 				return nil, fmt.Errorf("failed to parse gateway address (%v) returned by ipam driver: %v", gws, err)
 				return nil, fmt.Errorf("failed to parse gateway address (%v) returned by ipam driver: %v", gws, err)
 			}
 			}
@@ -917,7 +922,7 @@ func (na *cnmNetworkAllocator) allocatePools(n *api.Network) (map[string]string,
 		defer delete(dOptions, ipamapi.RequestAddressType)
 		defer delete(dOptions, ipamapi.RequestAddressType)
 
 
 		if ic.Gateway != "" || gwIP == nil {
 		if ic.Gateway != "" || gwIP == nil {
-			gwIP, _, err = ipam.RequestAddress(poolID, net.ParseIP(ic.Gateway), dOptions)
+			gwIP, _, err = ipam.RequestAddress(alloc.PoolID, net.ParseIP(ic.Gateway), dOptions)
 			if err != nil {
 			if err != nil {
 				// Rollback by releasing all the resources allocated so far.
 				// Rollback by releasing all the resources allocated so far.
 				releasePools(ipam, ipamConfigs[:i], pools)
 				releasePools(ipam, ipamConfigs[:i], pools)
@@ -926,7 +931,7 @@ func (na *cnmNetworkAllocator) allocatePools(n *api.Network) (map[string]string,
 		}
 		}
 
 
 		if ic.Subnet == "" {
 		if ic.Subnet == "" {
-			ic.Subnet = poolIP.String()
+			ic.Subnet = alloc.Pool.String()
 		}
 		}
 
 
 		if ic.Gateway == "" {
 		if ic.Gateway == "" {

+ 9 - 5
libnetwork/cnmallocator/networkallocator_test.go

@@ -3,9 +3,10 @@ package cnmallocator
 import (
 import (
 	"fmt"
 	"fmt"
 	"net"
 	"net"
+	"net/netip"
 	"testing"
 	"testing"
 
 
-	"github.com/docker/docker/libnetwork/types"
+	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/moby/swarmkit/v2/api"
 	"github.com/moby/swarmkit/v2/api"
 	"github.com/moby/swarmkit/v2/manager/allocator/networkallocator"
 	"github.com/moby/swarmkit/v2/manager/allocator/networkallocator"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/assert"
@@ -728,11 +729,14 @@ func (a *mockIpam) GetDefaultAddressSpaces() (string, string, error) {
 	return "defaultAS", "defaultAS", nil
 	return "defaultAS", "defaultAS", nil
 }
 }
 
 
-func (a *mockIpam) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
-	a.actualIpamOptions = options
+func (a *mockIpam) RequestPool(req ipamapi.PoolRequest) (ipamapi.AllocatedPool, error) {
+	a.actualIpamOptions = req.Options
 
 
-	poolCidr, _ := types.ParseCIDR(pool)
-	return fmt.Sprintf("%s/%s", "defaultAS", pool), poolCidr, nil, nil
+	poolCidr := netip.MustParsePrefix(req.Pool)
+	return ipamapi.AllocatedPool{
+		PoolID: fmt.Sprintf("defaultAS/%s", req.Pool),
+		Pool:   poolCidr,
+	}, nil
 }
 }
 
 
 func (a *mockIpam) ReleasePool(poolID string) error {
 func (a *mockIpam) ReleasePool(poolID string) error {

+ 20 - 26
libnetwork/ipam/allocator.go

@@ -91,41 +91,35 @@ func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) {
 // If requestedPool is the empty string then the default predefined pool for addressSpace will be used, otherwise pool must be a valid IP address and length in CIDR notation.
 // If requestedPool is the empty string then the default predefined pool for addressSpace will be used, otherwise pool must be a valid IP address and length in CIDR notation.
 // If requestedSubPool is not empty, it must be a valid IP address and length in CIDR notation which is a sub-range of requestedPool.
 // If requestedSubPool is not empty, it must be a valid IP address and length in CIDR notation which is a sub-range of requestedPool.
 // requestedSubPool must be empty if requestedPool is empty.
 // requestedSubPool must be empty if requestedPool is empty.
-func (a *Allocator) RequestPool(addressSpace, requestedPool, requestedSubPool string, _ map[string]string, v6 bool) (poolID string, pool *net.IPNet, meta map[string]string, err error) {
-	log.G(context.TODO()).Debugf("RequestPool(%s, %s, %s, _, %t)", addressSpace, requestedPool, requestedSubPool, v6)
+func (a *Allocator) RequestPool(req ipamapi.PoolRequest) (ipamapi.AllocatedPool, error) {
+	log.G(context.TODO()).Debugf("RequestPool: %+v", req)
 
 
-	parseErr := func(err error) error {
-		return types.InternalErrorf("failed to parse pool request for address space %q pool %q subpool %q: %v", addressSpace, requestedPool, requestedSubPool, err)
+	if req.AddressSpace == "" {
+		return ipamapi.AllocatedPool{}, types.InternalErrorf("invalid pool request: empty address space")
 	}
 	}
-
-	if addressSpace == "" {
-		return "", nil, nil, parseErr(ipamapi.ErrInvalidAddressSpace)
-	}
-	aSpace, err := a.getAddrSpace(addressSpace, v6)
+	aSpace, err := a.getAddrSpace(req.AddressSpace, req.V6)
 	if err != nil {
 	if err != nil {
-		return "", nil, nil, err
+		return ipamapi.AllocatedPool{}, types.InternalErrorf("invalid pool request: invalid address space")
 	}
 	}
-	if requestedPool == "" && requestedSubPool != "" {
-		return "", nil, nil, parseErr(ipamapi.ErrInvalidSubPool)
+	if req.Pool == "" && req.SubPool != "" {
+		return ipamapi.AllocatedPool{}, types.InternalErrorf("invalid pool request: %v", ipamapi.ErrInvalidSubPool)
 	}
 	}
 
 
-	k := PoolID{AddressSpace: addressSpace}
-	if requestedPool == "" {
-		k.Subnet, err = aSpace.allocatePredefinedPool(v6)
-		if err != nil {
-			return "", nil, nil, err
+	k := PoolID{AddressSpace: req.AddressSpace}
+	if req.Pool == "" {
+		if k.Subnet, err = aSpace.allocatePredefinedPool(req.V6); err != nil {
+			return ipamapi.AllocatedPool{}, err
 		}
 		}
-		return k.String(), netiputil.ToIPNet(k.Subnet), nil, nil
+		return ipamapi.AllocatedPool{PoolID: k.String(), Pool: k.Subnet}, nil
 	}
 	}
 
 
-	if k.Subnet, err = netip.ParsePrefix(requestedPool); err != nil {
-		return "", nil, nil, parseErr(ipamapi.ErrInvalidPool)
+	if k.Subnet, err = netip.ParsePrefix(req.Pool); err != nil {
+		return ipamapi.AllocatedPool{}, types.InternalErrorf("invalid pool request: %v", err)
 	}
 	}
 
 
-	if requestedSubPool != "" {
-		k.ChildSubnet, err = netip.ParsePrefix(requestedSubPool)
-		if err != nil {
-			return "", nil, nil, parseErr(ipamapi.ErrInvalidSubPool)
+	if req.SubPool != "" {
+		if k.ChildSubnet, err = netip.ParsePrefix(req.SubPool); err != nil {
+			return ipamapi.AllocatedPool{}, types.InternalErrorf("invalid pool request: %v", ipamapi.ErrInvalidSubPool)
 		}
 		}
 	}
 	}
 
 
@@ -140,10 +134,10 @@ func (a *Allocator) RequestPool(addressSpace, requestedPool, requestedSubPool st
 
 
 	err = aSpace.allocateSubnet(k.Subnet, k.ChildSubnet)
 	err = aSpace.allocateSubnet(k.Subnet, k.ChildSubnet)
 	if err != nil {
 	if err != nil {
-		return "", nil, nil, err
+		return ipamapi.AllocatedPool{}, types.ForbiddenErrorf("invalid pool request: %v", err)
 	}
 	}
 
 
-	return k.String(), netiputil.ToIPNet(k.Subnet), nil, nil
+	return ipamapi.AllocatedPool{PoolID: k.String(), Pool: k.Subnet}, nil
 }
 }
 
 
 // ReleasePool releases the address pool identified by the passed id
 // ReleasePool releases the address pool identified by the passed id

+ 135 - 137
libnetwork/ipam/allocator_test.go

@@ -14,6 +14,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/docker/docker/libnetwork/bitmap"
 	"github.com/docker/docker/libnetwork/bitmap"
+	"github.com/docker/docker/libnetwork/internal/netiputil"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/ipamutils"
 	"github.com/docker/docker/libnetwork/ipamutils"
 	"github.com/docker/docker/libnetwork/types"
 	"github.com/docker/docker/libnetwork/types"
@@ -58,55 +59,55 @@ func TestAddSubnets(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false)
+	alloc1, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.0.0.0/8"})
 	if err != nil {
 	if err != nil {
 		t.Fatal("Unexpected failure in adding subnet")
 		t.Fatal("Unexpected failure in adding subnet")
 	}
 	}
 
 
-	pid1, _, _, err := a.RequestPool(globalAddressSpace, "10.0.0.0/8", "", nil, false)
+	alloc2, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: globalAddressSpace, Pool: "10.0.0.0/8"})
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Unexpected failure in adding overlapping subnets to different address spaces: %v", err)
 		t.Fatalf("Unexpected failure in adding overlapping subnets to different address spaces: %v", err)
 	}
 	}
 
 
-	if pid0 == pid1 {
+	if alloc1.PoolID == alloc2.PoolID {
 		t.Fatal("returned same pool id for same subnets in different namespaces")
 		t.Fatal("returned same pool id for same subnets in different namespaces")
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(globalAddressSpace, "10.0.0.0/8", "", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: globalAddressSpace, Pool: "10.0.0.0/8"})
 	if err == nil {
 	if err == nil {
 		t.Fatalf("Expected failure requesting existing subnet")
 		t.Fatalf("Expected failure requesting existing subnet")
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(globalAddressSpace, "10.128.0.0/9", "", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: globalAddressSpace, Pool: "10.128.0.0/9"})
 	if err == nil {
 	if err == nil {
 		t.Fatal("Expected failure on adding overlapping base subnet")
 		t.Fatal("Expected failure on adding overlapping base subnet")
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(globalAddressSpace, "10.0.0.0/8", "10.128.0.0/9", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: globalAddressSpace, Pool: "10.0.0.0/8", SubPool: "10.128.0.0/9"})
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Unexpected failure on adding sub pool: %v", err)
 		t.Fatalf("Unexpected failure on adding sub pool: %v", err)
 	}
 	}
-	_, _, _, err = a.RequestPool(globalAddressSpace, "10.0.0.0/8", "10.128.0.0/9", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: globalAddressSpace, Pool: "10.0.0.0/8", SubPool: "10.128.0.0/9"})
 	if err == nil {
 	if err == nil {
 		t.Fatalf("Expected failure on adding overlapping sub pool")
 		t.Fatalf("Expected failure on adding overlapping sub pool")
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(localAddressSpace, "10.20.2.0/24", "", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.20.2.0/24"})
 	if err == nil {
 	if err == nil {
 		t.Fatal("Failed to detect overlapping subnets")
 		t.Fatal("Failed to detect overlapping subnets")
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(localAddressSpace, "10.128.0.0/9", "", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.128.0.0/9"})
 	if err == nil {
 	if err == nil {
 		t.Fatal("Failed to detect overlapping subnets")
 		t.Fatal("Failed to detect overlapping subnets")
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3:4:5:6::/112", "", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "1003:1:2:3:4:5:6::/112"})
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Failed to add v6 subnet: %s", err.Error())
 		t.Fatalf("Failed to add v6 subnet: %s", err.Error())
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3::/64", "", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "1003:1:2:3::/64"})
 	if err == nil {
 	if err == nil {
 		t.Fatal("Failed to detect overlapping v6 subnet")
 		t.Fatal("Failed to detect overlapping v6 subnet")
 	}
 	}
@@ -118,13 +119,13 @@ func TestDoublePoolRelease(t *testing.T) {
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false)
+	alloc1, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.0.0.0/8"})
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	err = a.ReleasePool(pid0)
+	err = a.ReleasePool(alloc1.PoolID)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	err = a.ReleasePool(pid0)
+	err = a.ReleasePool(alloc1.PoolID)
 	assert.Check(t, is.ErrorContains(err, ""))
 	assert.Check(t, is.ErrorContains(err, ""))
 }
 }
 
 
@@ -137,11 +138,11 @@ func TestAddReleasePoolID(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false)
+	alloc1, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.0.0.0/8"})
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Unexpected failure in adding pool: %v", err)
 		t.Fatalf("Unexpected failure in adding pool: %v", err)
 	}
 	}
-	k0, err := PoolIDFromString(pid0)
+	k0, err := PoolIDFromString(alloc1.PoolID)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -155,17 +156,17 @@ func TestAddReleasePoolID(t *testing.T) {
 		t.Errorf("Unexpected autoRelease value for %s: %v", k0, got)
 		t.Errorf("Unexpected autoRelease value for %s: %v", k0, got)
 	}
 	}
 
 
-	pid1, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false)
+	alloc2, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.0.0.0/8", SubPool: "10.0.0.0/16"})
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Unexpected failure in adding sub pool: %v", err)
 		t.Fatalf("Unexpected failure in adding sub pool: %v", err)
 	}
 	}
-	k1, err := PoolIDFromString(pid1)
+	k1, err := PoolIDFromString(alloc2.PoolID)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if pid0 == pid1 {
-		t.Fatalf("Incorrect poolIDs returned %s, %s", pid0, pid1)
+	if alloc1.PoolID == alloc2.PoolID {
+		t.Fatalf("Incorrect poolIDs returned %s, %s", alloc1.PoolID, alloc2.PoolID)
 	}
 	}
 
 
 	aSpace, err = a.getAddrSpace(localAddressSpace, false)
 	aSpace, err = a.getAddrSpace(localAddressSpace, false)
@@ -177,7 +178,7 @@ func TestAddReleasePoolID(t *testing.T) {
 		t.Errorf("Unexpected autoRelease value for %s: %v", k1, got)
 		t.Errorf("Unexpected autoRelease value for %s: %v", k1, got)
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.0.0.0/8", SubPool: "10.0.0.0/16"})
 	if err == nil {
 	if err == nil {
 		t.Fatalf("Expected failure in adding sub pool: %v", err)
 		t.Fatalf("Expected failure in adding sub pool: %v", err)
 	}
 	}
@@ -191,7 +192,7 @@ func TestAddReleasePoolID(t *testing.T) {
 		t.Errorf("Unexpected autoRelease value for %s: %v", k0, got)
 		t.Errorf("Unexpected autoRelease value for %s: %v", k0, got)
 	}
 	}
 
 
-	if err := a.ReleasePool(pid1); err != nil {
+	if err := a.ReleasePool(alloc2.PoolID); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
@@ -203,7 +204,7 @@ func TestAddReleasePoolID(t *testing.T) {
 	if got := aSpace.subnets[k0.Subnet].autoRelease; got != false {
 	if got := aSpace.subnets[k0.Subnet].autoRelease; got != false {
 		t.Errorf("Unexpected autoRelease value for %s: %v", k0, got)
 		t.Errorf("Unexpected autoRelease value for %s: %v", k0, got)
 	}
 	}
-	if err := a.ReleasePool(pid0); err != nil {
+	if err := a.ReleasePool(alloc1.PoolID); err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
 
 
@@ -211,12 +212,12 @@ func TestAddReleasePoolID(t *testing.T) {
 		t.Error("Pool should have been deleted when released")
 		t.Error("Pool should have been deleted when released")
 	}
 	}
 
 
-	pid00, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false)
+	alloc10, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.0.0.0/8"})
 	if err != nil {
 	if err != nil {
 		t.Errorf("Unexpected failure in adding pool: %v", err)
 		t.Errorf("Unexpected failure in adding pool: %v", err)
 	}
 	}
-	if pid00 != pid0 {
-		t.Errorf("main pool should still exist. Got poolID %q, want %q", pid00, pid0)
+	if alloc10.PoolID != alloc1.PoolID {
+		t.Errorf("main pool should still exist. Got poolID %q, want %q", alloc10.PoolID, alloc1.PoolID)
 	}
 	}
 
 
 	aSpace, err = a.getAddrSpace(localAddressSpace, false)
 	aSpace, err = a.getAddrSpace(localAddressSpace, false)
@@ -228,7 +229,7 @@ func TestAddReleasePoolID(t *testing.T) {
 		t.Errorf("Unexpected autoRelease value for %s: %v", k0, got)
 		t.Errorf("Unexpected autoRelease value for %s: %v", k0, got)
 	}
 	}
 
 
-	if err := a.ReleasePool(pid00); err != nil {
+	if err := a.ReleasePool(alloc10.PoolID); err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
 
 
@@ -241,7 +242,7 @@ func TestAddReleasePoolID(t *testing.T) {
 		t.Errorf("Base pool %s is still present: %v", k0, bp)
 		t.Errorf("Base pool %s is still present: %v", k0, bp)
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.0.0.0/8"})
 	if err != nil {
 	if err != nil {
 		t.Errorf("Unexpected failure in adding pool: %v", err)
 		t.Errorf("Unexpected failure in adding pool: %v", err)
 	}
 	}
@@ -260,25 +261,25 @@ func TestPredefinedPool(t *testing.T) {
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	pid, nw, _, err := a.RequestPool(localAddressSpace, "", "", nil, false)
+	alloc1, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	pid2, nw2, _, err := a.RequestPool(localAddressSpace, "", "", nil, false)
+	alloc2, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if types.CompareIPNet(nw, nw2) {
-		t.Fatalf("Unexpected default network returned: %s = %s", nw2, nw)
+	if alloc1.Pool == alloc2.Pool {
+		t.Fatalf("Unexpected default network returned: %s = %s", alloc2.Pool, alloc1.Pool)
 	}
 	}
 
 
-	if err := a.ReleasePool(pid); err != nil {
+	if err := a.ReleasePool(alloc1.PoolID); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	if err := a.ReleasePool(pid2); err != nil {
+	if err := a.ReleasePool(alloc2.PoolID); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 }
 }
@@ -287,32 +288,29 @@ func TestRemoveSubnet(t *testing.T) {
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	input := []struct {
-		addrSpace string
-		subnet    string
-		v6        bool
-	}{
-		{localAddressSpace, "192.168.0.0/16", false},
-		{localAddressSpace, "172.17.0.0/16", false},
-		{localAddressSpace, "10.0.0.0/8", false},
-		{localAddressSpace, "2001:db8:1:2:3:4:ffff::/112", true},
-		{globalAddressSpace, "172.17.0.0/16", false},
-		{globalAddressSpace, "10.0.0.0/8", false},
-		{globalAddressSpace, "2001:db8:1:2:3:4:5::/112", true},
-		{globalAddressSpace, "2001:db8:1:2:3:4:ffff::/112", true},
+	reqs := []ipamapi.PoolRequest{
+		{AddressSpace: localAddressSpace, Pool: "192.168.0.0/16"},
+		{AddressSpace: localAddressSpace, Pool: "172.17.0.0/16"},
+		{AddressSpace: localAddressSpace, Pool: "10.0.0.0/8"},
+		{AddressSpace: localAddressSpace, Pool: "2001:db8:1:2:3:4:ffff::/112", V6: true},
+		{AddressSpace: globalAddressSpace, Pool: "172.17.0.0/16"},
+		{AddressSpace: globalAddressSpace, Pool: "10.0.0.0/8"},
+		{AddressSpace: globalAddressSpace, Pool: "2001:db8:1:2:3:4:5::/112", V6: true},
+		{AddressSpace: globalAddressSpace, Pool: "2001:db8:1:2:3:4:ffff::/112", V6: true},
 	}
 	}
+	allocs := make([]ipamapi.AllocatedPool, 0, len(reqs))
 
 
-	poolIDs := make([]string, len(input))
-
-	for ind, i := range input {
-		if poolIDs[ind], _, _, err = a.RequestPool(i.addrSpace, i.subnet, "", nil, i.v6); err != nil {
+	for _, req := range reqs {
+		alloc, err := a.RequestPool(req)
+		if err != nil {
 			t.Fatalf("Failed to apply input. Can't proceed: %s", err.Error())
 			t.Fatalf("Failed to apply input. Can't proceed: %s", err.Error())
 		}
 		}
+		allocs = append(allocs, alloc)
 	}
 	}
 
 
-	for ind, id := range poolIDs {
-		if err := a.ReleasePool(id); err != nil {
-			t.Fatalf("Failed to release poolID %s (%d)", id, ind)
+	for idx, alloc := range allocs {
+		if err := a.ReleasePool(alloc.PoolID); err != nil {
+			t.Fatalf("Failed to release poolID %s (%d)", alloc.PoolID, idx)
 		}
 		}
 	}
 	}
 }
 }
@@ -321,18 +319,18 @@ func TestGetSameAddress(t *testing.T) {
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	pid, _, _, err := a.RequestPool(localAddressSpace, "192.168.100.0/24", "", nil, false)
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "192.168.100.0/24"})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
 	ip := net.ParseIP("192.168.100.250")
 	ip := net.ParseIP("192.168.100.250")
-	_, _, err = a.RequestAddress(pid, ip, nil)
+	_, _, err = a.RequestAddress(alloc.PoolID, ip, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	_, _, err = a.RequestAddress(pid, ip, nil)
+	_, _, err = a.RequestAddress(alloc.PoolID, ip, nil)
 	if err == nil {
 	if err == nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -343,15 +341,14 @@ func TestPoolAllocationReuse(t *testing.T) {
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	// First get all pools until they are exhausted to
 	// First get all pools until they are exhausted to
-	pList := []string{}
-	pool, _, _, err := a.RequestPool(localAddressSpace, "", "", nil, false)
+	allocs := []ipamapi.AllocatedPool{}
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace})
 	for err == nil {
 	for err == nil {
-		pList = append(pList, pool)
-		pool, _, _, err = a.RequestPool(localAddressSpace, "", "", nil, false)
+		allocs = append(allocs, alloc)
+		alloc, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace})
 	}
 	}
-	nPools := len(pList)
-	for _, pool := range pList {
-		if err := a.ReleasePool(pool); err != nil {
+	for _, alloc := range allocs {
+		if err := a.ReleasePool(alloc.PoolID); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 	}
 	}
@@ -360,16 +357,16 @@ func TestPoolAllocationReuse(t *testing.T) {
 	// Verify that we don't see any repeat networks even though
 	// Verify that we don't see any repeat networks even though
 	// we have freed them.
 	// we have freed them.
 	seen := map[string]bool{}
 	seen := map[string]bool{}
-	for i := 0; i < nPools; i++ {
-		pool, nw, _, err := a.RequestPool(localAddressSpace, "", "", nil, false)
+	for i := 0; i < len(allocs); i++ {
+		alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace})
 		if err != nil {
 		if err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
-		if _, ok := seen[nw.String()]; ok {
-			t.Fatalf("Network %s was reused before exhausing the pool list", nw.String())
+		if _, ok := seen[alloc.Pool.String()]; ok {
+			t.Fatalf("Network %s was reused before exhausing the pool list", alloc.Pool.String())
 		}
 		}
-		seen[nw.String()] = true
-		if err := a.ReleasePool(pool); err != nil {
+		seen[alloc.Pool.String()] = true
+		if err := a.ReleasePool(alloc.PoolID); err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
 	}
 	}
@@ -380,12 +377,12 @@ func TestGetAddressSubPoolEqualPool(t *testing.T) {
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
 	// Requesting a subpool of same size of the master pool should not cause any problem on ip allocation
 	// Requesting a subpool of same size of the master pool should not cause any problem on ip allocation
-	pid, _, _, err := a.RequestPool(localAddressSpace, "172.18.0.0/16", "172.18.0.0/16", nil, false)
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "172.18.0.0/16", SubPool: "172.18.0.0/16"})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	_, _, err = a.RequestAddress(pid, nil, nil)
+	_, _, err = a.RequestAddress(alloc.PoolID, nil, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -395,7 +392,7 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	poolID, _, _, err := a.RequestPool(localAddressSpace, "172.28.0.0/16", "172.28.30.0/24", nil, false)
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "172.28.0.0/16", SubPool: "172.28.30.0/24"})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -404,7 +401,7 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 	expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}}
 	expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}}
 	for err == nil {
 	for err == nil {
 		var c *net.IPNet
 		var c *net.IPNet
-		if c, _, err = a.RequestAddress(poolID, nil, nil); err == nil {
+		if c, _, err = a.RequestAddress(alloc.PoolID, nil, nil); err == nil {
 			ip = c
 			ip = c
 		}
 		}
 	}
 	}
@@ -415,28 +412,28 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip)
 		t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip)
 	}
 	}
 	rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}}
 	rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}}
-	if err = a.ReleaseAddress(poolID, rp.IP); err != nil {
+	if err = a.ReleaseAddress(alloc.PoolID, rp.IP); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if ip, _, err = a.RequestAddress(poolID, nil, nil); err != nil {
+	if ip, _, err = a.RequestAddress(alloc.PoolID, nil, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if !types.CompareIPNet(rp, ip) {
 	if !types.CompareIPNet(rp, ip) {
 		t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip)
 		t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip)
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.0.0.0/8", SubPool: "10.0.0.0/16"})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	poolID, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/16", "10.0.0.0/24", nil, false)
+	alloc, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.0.0.0/16", SubPool: "10.0.0.0/24"})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}}
 	expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}}
 	for err == nil {
 	for err == nil {
 		var c *net.IPNet
 		var c *net.IPNet
-		if c, _, err = a.RequestAddress(poolID, nil, nil); err == nil {
+		if c, _, err = a.RequestAddress(alloc.PoolID, nil, nil); err == nil {
 			ip = c
 			ip = c
 		}
 		}
 	}
 	}
@@ -447,10 +444,10 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip)
 		t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip)
 	}
 	}
 	rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}}
 	rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}}
-	if err = a.ReleaseAddress(poolID, rp.IP); err != nil {
+	if err = a.ReleaseAddress(alloc.PoolID, rp.IP); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if ip, _, err = a.RequestAddress(poolID, nil, nil); err != nil {
+	if ip, _, err = a.RequestAddress(alloc.PoolID, nil, nil); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if !types.CompareIPNet(rp, ip) {
 	if !types.CompareIPNet(rp, ip) {
@@ -462,10 +459,10 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 	dueExp, _ := types.ParseCIDR("10.2.2.2/16")
 	dueExp, _ := types.ParseCIDR("10.2.2.2/16")
 	treExp, _ := types.ParseCIDR("10.2.2.1/16")
 	treExp, _ := types.ParseCIDR("10.2.2.1/16")
 
 
-	if poolID, _, _, err = a.RequestPool(localAddressSpace, "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil {
+	if alloc, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.2.0.0/16", SubPool: "10.2.2.0/24"}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	tre, _, err := a.RequestAddress(poolID, treExp.IP, nil)
+	tre, _, err := a.RequestAddress(alloc.PoolID, treExp.IP, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -473,7 +470,7 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected address: want %v, got %v", treExp, tre)
 		t.Fatalf("Unexpected address: want %v, got %v", treExp, tre)
 	}
 	}
 
 
-	uno, _, err := a.RequestAddress(poolID, nil, nil)
+	uno, _, err := a.RequestAddress(alloc.PoolID, nil, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -481,7 +478,7 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected address: %v", uno)
 		t.Fatalf("Unexpected address: %v", uno)
 	}
 	}
 
 
-	due, _, err := a.RequestAddress(poolID, nil, nil)
+	due, _, err := a.RequestAddress(alloc.PoolID, nil, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -489,10 +486,10 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected address: %v", due)
 		t.Fatalf("Unexpected address: %v", due)
 	}
 	}
 
 
-	if err = a.ReleaseAddress(poolID, uno.IP); err != nil {
+	if err = a.ReleaseAddress(alloc.PoolID, uno.IP); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	uno, _, err = a.RequestAddress(poolID, nil, nil)
+	uno, _, err = a.RequestAddress(alloc.PoolID, nil, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -500,10 +497,10 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected address: %v", uno)
 		t.Fatalf("Unexpected address: %v", uno)
 	}
 	}
 
 
-	if err = a.ReleaseAddress(poolID, tre.IP); err != nil {
+	if err = a.ReleaseAddress(alloc.PoolID, tre.IP); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	tre, _, err = a.RequestAddress(poolID, nil, nil)
+	tre, _, err = a.RequestAddress(alloc.PoolID, nil, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -519,7 +516,7 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	poolID, _, _, err := a.RequestPool(localAddressSpace, "172.28.0.0/16", "172.28.30.0/24", nil, false)
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "172.28.0.0/16", SubPool: "172.28.30.0/24"})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -528,7 +525,7 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 	expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}}
 	expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}}
 	for err == nil {
 	for err == nil {
 		var c *net.IPNet
 		var c *net.IPNet
-		if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil {
+		if c, _, err = a.RequestAddress(alloc.PoolID, nil, opts); err == nil {
 			ip = c
 			ip = c
 		}
 		}
 	}
 	}
@@ -539,28 +536,28 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip)
 		t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip)
 	}
 	}
 	rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}}
 	rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}}
-	if err = a.ReleaseAddress(poolID, rp.IP); err != nil {
+	if err = a.ReleaseAddress(alloc.PoolID, rp.IP); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if ip, _, err = a.RequestAddress(poolID, nil, opts); err != nil {
+	if ip, _, err = a.RequestAddress(alloc.PoolID, nil, opts); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if !types.CompareIPNet(rp, ip) {
 	if !types.CompareIPNet(rp, ip) {
 		t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip)
 		t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip)
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.0.0.0/8", SubPool: "10.0.0.0/16"})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	poolID, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/16", "10.0.0.0/24", nil, false)
+	alloc, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.0.0.0/16", SubPool: "10.0.0.0/24"})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}}
 	expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}}
 	for err == nil {
 	for err == nil {
 		var c *net.IPNet
 		var c *net.IPNet
-		if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil {
+		if c, _, err = a.RequestAddress(alloc.PoolID, nil, opts); err == nil {
 			ip = c
 			ip = c
 		}
 		}
 	}
 	}
@@ -571,10 +568,10 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip)
 		t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip)
 	}
 	}
 	rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}}
 	rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}}
-	if err = a.ReleaseAddress(poolID, rp.IP); err != nil {
+	if err = a.ReleaseAddress(alloc.PoolID, rp.IP); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	if ip, _, err = a.RequestAddress(poolID, nil, opts); err != nil {
+	if ip, _, err = a.RequestAddress(alloc.PoolID, nil, opts); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if !types.CompareIPNet(rp, ip) {
 	if !types.CompareIPNet(rp, ip) {
@@ -587,10 +584,10 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 	treExp, _ := types.ParseCIDR("10.2.2.1/16")
 	treExp, _ := types.ParseCIDR("10.2.2.1/16")
 	quaExp, _ := types.ParseCIDR("10.2.2.3/16")
 	quaExp, _ := types.ParseCIDR("10.2.2.3/16")
 	fivExp, _ := types.ParseCIDR("10.2.2.4/16")
 	fivExp, _ := types.ParseCIDR("10.2.2.4/16")
-	if poolID, _, _, err = a.RequestPool(localAddressSpace, "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil {
+	if alloc, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "10.2.0.0/16", SubPool: "10.2.2.0/24"}); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	tre, _, err := a.RequestAddress(poolID, treExp.IP, opts)
+	tre, _, err := a.RequestAddress(alloc.PoolID, treExp.IP, opts)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -598,7 +595,7 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected address: want %v, got %v", treExp, tre)
 		t.Fatalf("Unexpected address: want %v, got %v", treExp, tre)
 	}
 	}
 
 
-	uno, _, err := a.RequestAddress(poolID, nil, opts)
+	uno, _, err := a.RequestAddress(alloc.PoolID, nil, opts)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -606,7 +603,7 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected address: %v", uno)
 		t.Fatalf("Unexpected address: %v", uno)
 	}
 	}
 
 
-	due, _, err := a.RequestAddress(poolID, nil, opts)
+	due, _, err := a.RequestAddress(alloc.PoolID, nil, opts)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -614,10 +611,10 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected address: %v", due)
 		t.Fatalf("Unexpected address: %v", due)
 	}
 	}
 
 
-	if err = a.ReleaseAddress(poolID, uno.IP); err != nil {
+	if err = a.ReleaseAddress(alloc.PoolID, uno.IP); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	uno, _, err = a.RequestAddress(poolID, nil, opts)
+	uno, _, err = a.RequestAddress(alloc.PoolID, nil, opts)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -625,10 +622,10 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 		t.Fatalf("Unexpected address: %v", uno)
 		t.Fatalf("Unexpected address: %v", uno)
 	}
 	}
 
 
-	if err = a.ReleaseAddress(poolID, tre.IP); err != nil {
+	if err = a.ReleaseAddress(alloc.PoolID, tre.IP); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	tre, _, err = a.RequestAddress(poolID, nil, opts)
+	tre, _, err = a.RequestAddress(alloc.PoolID, nil, opts)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -659,22 +656,22 @@ func TestRequestSyntaxCheck(t *testing.T) {
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	_, _, _, err = a.RequestPool("", pool, "", nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{Pool: pool})
 	if err == nil {
 	if err == nil {
 		t.Fatal("Failed to detect wrong request: empty address space")
 		t.Fatal("Failed to detect wrong request: empty address space")
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool("", pool, subPool, nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{Pool: pool, SubPool: subPool})
 	if err == nil {
 	if err == nil {
 		t.Fatal("Failed to detect wrong request: empty address space")
 		t.Fatal("Failed to detect wrong request: empty address space")
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(localAddressSpace, "", subPool, nil, false)
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, SubPool: subPool})
 	if err == nil {
 	if err == nil {
 		t.Fatal("Failed to detect wrong request: subPool specified and no pool")
 		t.Fatal("Failed to detect wrong request: subPool specified and no pool")
 	}
 	}
 
 
-	pid, _, _, err := a.RequestPool(localAddressSpace, pool, subPool, nil, false)
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: pool, SubPool: subPool})
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Unexpected failure: %v", err)
 		t.Fatalf("Unexpected failure: %v", err)
 	}
 	}
@@ -685,13 +682,13 @@ func TestRequestSyntaxCheck(t *testing.T) {
 	}
 	}
 
 
 	ip := net.ParseIP("172.17.0.23")
 	ip := net.ParseIP("172.17.0.23")
-	_, _, err = a.RequestAddress(pid, ip, nil)
+	_, _, err = a.RequestAddress(alloc.PoolID, ip, nil)
 	if err == nil {
 	if err == nil {
 		t.Fatal("Failed to detect wrong request: requested IP from different subnet")
 		t.Fatal("Failed to detect wrong request: requested IP from different subnet")
 	}
 	}
 
 
 	ip = net.ParseIP("192.168.0.50")
 	ip = net.ParseIP("192.168.0.50")
-	_, _, err = a.RequestAddress(pid, ip, nil)
+	_, _, err = a.RequestAddress(alloc.PoolID, ip, nil)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Unexpected failure: %v", err)
 		t.Fatalf("Unexpected failure: %v", err)
 	}
 	}
@@ -701,14 +698,14 @@ func TestRequestSyntaxCheck(t *testing.T) {
 		t.Fatal("Failed to detect wrong request: no pool id specified")
 		t.Fatal("Failed to detect wrong request: no pool id specified")
 	}
 	}
 
 
-	err = a.ReleaseAddress(pid, nil)
+	err = a.ReleaseAddress(alloc.PoolID, nil)
 	if err == nil {
 	if err == nil {
 		t.Fatal("Failed to detect wrong request: no pool id specified")
 		t.Fatal("Failed to detect wrong request: no pool id specified")
 	}
 	}
 
 
-	err = a.ReleaseAddress(pid, ip)
+	err = a.ReleaseAddress(alloc.PoolID, ip)
 	if err != nil {
 	if err != nil {
-		t.Fatalf("Unexpected failure: %v: %s, %s", err, pid, ip)
+		t.Fatalf("Unexpected failure: %v: %s, %s", err, alloc.PoolID, ip)
 	}
 	}
 }
 }
 
 
@@ -807,12 +804,12 @@ func TestOverlappingRequests(t *testing.T) {
 
 
 		// Set up some existing allocations.  This should always succeed.
 		// Set up some existing allocations.  This should always succeed.
 		for _, env := range tc.environment {
 		for _, env := range tc.environment {
-			_, _, _, err = a.RequestPool(localAddressSpace, env, "", nil, false)
+			_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: env})
 			assert.NilError(t, err)
 			assert.NilError(t, err)
 		}
 		}
 
 
 		// Make the test allocation.
 		// Make the test allocation.
-		_, _, _, err = a.RequestPool(localAddressSpace, tc.subnet, "", nil, false)
+		_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: tc.subnet})
 		if tc.ok {
 		if tc.ok {
 			assert.NilError(t, err)
 			assert.NilError(t, err)
 		} else {
 		} else {
@@ -848,7 +845,7 @@ func TestUnusualSubnets(t *testing.T) {
 	// IPv4 /31 blocks.  See RFC 3021.
 	// IPv4 /31 blocks.  See RFC 3021.
 	//
 	//
 
 
-	pool, _, _, err := allocator.RequestPool(localAddressSpace, subnet, "", nil, false)
+	alloc, err := allocator.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: subnet})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -856,7 +853,7 @@ func TestUnusualSubnets(t *testing.T) {
 	// Outside-the-range
 	// Outside-the-range
 
 
 	for _, outside := range outsideTheRangeAddresses {
 	for _, outside := range outsideTheRangeAddresses {
-		_, _, errx := allocator.RequestAddress(pool, net.ParseIP(outside.address), nil)
+		_, _, errx := allocator.RequestAddress(alloc.PoolID, net.ParseIP(outside.address), nil)
 		if errx != ipamapi.ErrIPOutOfRange {
 		if errx != ipamapi.ErrIPOutOfRange {
 			t.Fatalf("Address %s failed to throw expected error: %s", outside.address, errx.Error())
 			t.Fatalf("Address %s failed to throw expected error: %s", outside.address, errx.Error())
 		}
 		}
@@ -865,7 +862,7 @@ func TestUnusualSubnets(t *testing.T) {
 	// Should get just these two IPs followed by exhaustion on the next request
 	// Should get just these two IPs followed by exhaustion on the next request
 
 
 	for _, expected := range expectedAddresses {
 	for _, expected := range expectedAddresses {
-		got, _, errx := allocator.RequestAddress(pool, nil, nil)
+		got, _, errx := allocator.RequestAddress(alloc.PoolID, nil, nil)
 		if errx != nil {
 		if errx != nil {
 			t.Fatalf("Failed to obtain the address: %s", errx.Error())
 			t.Fatalf("Failed to obtain the address: %s", errx.Error())
 		}
 		}
@@ -876,7 +873,7 @@ func TestUnusualSubnets(t *testing.T) {
 		}
 		}
 	}
 	}
 
 
-	_, _, err = allocator.RequestAddress(pool, nil, nil)
+	_, _, err = allocator.RequestAddress(alloc.PoolID, nil, nil)
 	if err != ipamapi.ErrNoAvailableIPs {
 	if err != ipamapi.ErrNoAvailableIPs {
 		t.Fatal("Did not get expected error when pool is exhausted.")
 		t.Fatal("Did not get expected error when pool is exhausted.")
 	}
 	}
@@ -888,14 +885,14 @@ func TestRelease(t *testing.T) {
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false)
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: subnet})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
 	// Allocate all addresses
 	// Allocate all addresses
 	for err != ipamapi.ErrNoAvailableIPs {
 	for err != ipamapi.ErrNoAvailableIPs {
-		_, _, err = a.RequestAddress(pid, nil, nil)
+		_, _, err = a.RequestAddress(alloc.PoolID, nil, nil)
 	}
 	}
 
 
 	toRelease := []struct {
 	toRelease := []struct {
@@ -928,13 +925,13 @@ func TestRelease(t *testing.T) {
 	// One by one, release the address and request again. We should get the same IP
 	// One by one, release the address and request again. We should get the same IP
 	for i, inp := range toRelease {
 	for i, inp := range toRelease {
 		ip0 := net.ParseIP(inp.address)
 		ip0 := net.ParseIP(inp.address)
-		a.ReleaseAddress(pid, ip0)
+		a.ReleaseAddress(alloc.PoolID, ip0)
 		bm := a.local4.subnets[netip.MustParsePrefix(subnet)].addrs
 		bm := a.local4.subnets[netip.MustParsePrefix(subnet)].addrs
 		if bm.Unselected() != 1 {
 		if bm.Unselected() != 1 {
 			t.Fatalf("Failed to update free address count after release. Expected %d, Found: %d", i+1, bm.Unselected())
 			t.Fatalf("Failed to update free address count after release. Expected %d, Found: %d", i+1, bm.Unselected())
 		}
 		}
 
 
-		nw, _, err := a.RequestAddress(pid, nil, nil)
+		nw, _, err := a.RequestAddress(alloc.PoolID, nil, nil)
 		if err != nil {
 		if err != nil {
 			t.Fatalf("Failed to obtain the address: %s", err.Error())
 			t.Fatalf("Failed to obtain the address: %s", err.Error())
 		}
 		}
@@ -988,7 +985,7 @@ func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP str
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false)
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: subnet})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -996,7 +993,7 @@ func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP str
 	i := 0
 	i := 0
 	start := time.Now()
 	start := time.Now()
 	for ; i < numReq; i++ {
 	for ; i < numReq; i++ {
-		nw, _, err = a.RequestAddress(pid, nil, nil)
+		nw, _, err = a.RequestAddress(alloc.PoolID, nil, nil)
 		if err != nil {
 		if err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
@@ -1011,9 +1008,9 @@ func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP str
 }
 }
 
 
 func benchmarkRequest(b *testing.B, a *Allocator, subnet string) {
 func benchmarkRequest(b *testing.B, a *Allocator, subnet string) {
-	pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false)
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: subnet})
 	for err != ipamapi.ErrNoAvailableIPs {
 	for err != ipamapi.ErrNoAvailableIPs {
-		_, _, err = a.RequestAddress(pid, nil, nil)
+		_, _, err = a.RequestAddress(alloc.PoolID, nil, nil)
 	}
 	}
 }
 }
 
 
@@ -1046,7 +1043,7 @@ func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int, s
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	pid, _, _, err := a.RequestPool(localAddressSpace, pool, subPool, nil, false)
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: pool, SubPool: subPool})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1055,7 +1052,7 @@ func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int, s
 	indices := make(map[int]*net.IPNet, num)
 	indices := make(map[int]*net.IPNet, num)
 	allocated := make(map[string]bool, num)
 	allocated := make(map[string]bool, num)
 	for i := 0; i < num; i++ {
 	for i := 0; i < num; i++ {
-		ip, _, err := a.RequestAddress(pid, nil, nil)
+		ip, _, err := a.RequestAddress(alloc.PoolID, nil, nil)
 		if err != nil {
 		if err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
@@ -1078,7 +1075,7 @@ func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int, s
 	for i := 0; i < num/2; i++ {
 	for i := 0; i < num/2; i++ {
 		idx := pattern[i]
 		idx := pattern[i]
 		ip := indices[idx]
 		ip := indices[idx]
-		err := a.ReleaseAddress(pid, ip.IP)
+		err := a.ReleaseAddress(alloc.PoolID, ip.IP)
 		if err != nil {
 		if err != nil {
 			t.Fatalf("Unexpected failure on deallocation of %s: %v.\nSeed: %d.", ip, err, seed)
 			t.Fatalf("Unexpected failure on deallocation of %s: %v.\nSeed: %d.", ip, err, seed)
 		}
 		}
@@ -1088,7 +1085,7 @@ func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int, s
 
 
 	// Request a quarter of addresses
 	// Request a quarter of addresses
 	for i := 0; i < num/2; i++ {
 	for i := 0; i < num/2; i++ {
-		ip, _, err := a.RequestAddress(pid, nil, nil)
+		ip, _, err := a.RequestAddress(alloc.PoolID, nil, nil)
 		if err != nil {
 		if err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
@@ -1148,7 +1145,8 @@ func runParallelTests(t *testing.T, instance int) {
 		defer done.Done()
 		defer done.Done()
 	}
 	}
 
 
-	_, pools[instance], _, err = allocator.RequestPool(localAddressSpace, "", "", nil, false)
+	alloc, err := allocator.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace})
+	pools[instance] = netiputil.ToIPNet(alloc.Pool)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1183,7 +1181,7 @@ func TestRequestReleaseAddressDuplicate(t *testing.T) {
 	}
 	}
 	var l sync.Mutex
 	var l sync.Mutex
 
 
-	poolID, _, _, err := a.RequestPool(localAddressSpace, "198.168.0.0/23", "", nil, false)
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: "198.168.0.0/23"})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1203,7 +1201,7 @@ outer:
 				break outer
 				break outer
 			default:
 			default:
 			}
 			}
-			if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil {
+			if c, _, err = a.RequestAddress(alloc.PoolID, nil, opts); err == nil {
 				break
 				break
 			}
 			}
 			// No addresses available. Spin until one is.
 			// No addresses available. Spin until one is.
@@ -1236,7 +1234,7 @@ outer:
 				l.Lock()
 				l.Lock()
 				ips = append(ips, IP{ip, -1})
 				ips = append(ips, IP{ip, -1})
 				l.Unlock()
 				l.Unlock()
-				return a.ReleaseAddress(poolID, ip.IP)
+				return a.ReleaseAddress(alloc.PoolID, ip.IP)
 			})
 			})
 		}
 		}
 	}
 	}

+ 6 - 6
libnetwork/ipam/parallel_test.go

@@ -45,7 +45,7 @@ func newTestContext(t *testing.T, mask int, options map[string]string) *testCont
 	// total ips 2^(32-mask) - 2 (network and broadcast)
 	// total ips 2^(32-mask) - 2 (network and broadcast)
 	totalIps := 1<<uint(32-mask) - 2
 	totalIps := 1<<uint(32-mask) - 2
 
 
-	pid, _, _, err := a.RequestPool(localAddressSpace, network, "", nil, false)
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace, Pool: network})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -55,7 +55,7 @@ func newTestContext(t *testing.T, mask int, options map[string]string) *testCont
 		opts:   options,
 		opts:   options,
 		ipList: make([]*net.IPNet, 0, totalIps),
 		ipList: make([]*net.IPNet, 0, totalIps),
 		ipMap:  make(map[string]bool),
 		ipMap:  make(map[string]bool),
-		pid:    pid,
+		pid:    alloc.PoolID,
 		maxIP:  totalIps,
 		maxIP:  totalIps,
 	}
 	}
 }
 }
@@ -93,21 +93,21 @@ func TestRequestPoolParallel(t *testing.T) {
 
 
 	for i := 0; i < 120; i++ {
 	for i := 0; i < 120; i++ {
 		group.Go(func() error {
 		group.Go(func() error {
-			name, _, _, err := a.RequestPool("GlobalDefault", "", "", nil, false)
+			alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: "GlobalDefault"})
 			if err != nil {
 			if err != nil {
 				t.Log(err) // log so we can see the error in real time rather than at the end when we actually call "Wait".
 				t.Log(err) // log so we can see the error in real time rather than at the end when we actually call "Wait".
 				return fmt.Errorf("request error %v", err)
 				return fmt.Errorf("request error %v", err)
 			}
 			}
 			idx := atomic.AddInt32(&operationIndex, 1)
 			idx := atomic.AddInt32(&operationIndex, 1)
-			ch <- &op{idx, true, name}
+			ch <- &op{idx, true, alloc.PoolID}
 			time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
 			time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
 			idx = atomic.AddInt32(&operationIndex, 1)
 			idx = atomic.AddInt32(&operationIndex, 1)
-			err = a.ReleasePool(name)
+			err = a.ReleasePool(alloc.PoolID)
 			if err != nil {
 			if err != nil {
 				t.Log(err) // log so we can see the error in real time rather than at the end when we actually call "Wait".
 				t.Log(err) // log so we can see the error in real time rather than at the end when we actually call "Wait".
 				return fmt.Errorf("release error %v", err)
 				return fmt.Errorf("release error %v", err)
 			}
 			}
-			ch <- &op{idx, false, name}
+			ch <- &op{idx, false, alloc.PoolID}
 			return nil
 			return nil
 		})
 		})
 	}
 	}

+ 39 - 7
libnetwork/ipamapi/contract.go

@@ -3,6 +3,7 @@ package ipamapi
 
 
 import (
 import (
 	"net"
 	"net"
+	"net/netip"
 
 
 	"github.com/docker/docker/libnetwork/types"
 	"github.com/docker/docker/libnetwork/types"
 )
 )
@@ -45,13 +46,9 @@ var (
 type Ipam interface {
 type Ipam interface {
 	// GetDefaultAddressSpaces returns the default local and global address spaces for this ipam
 	// GetDefaultAddressSpaces returns the default local and global address spaces for this ipam
 	GetDefaultAddressSpaces() (string, string, error)
 	GetDefaultAddressSpaces() (string, string, error)
-	// RequestPool returns an address pool along with its unique id. Address space is a mandatory field
-	// which denotes a set of non-overlapping pools. requestedPool describes the pool of addresses in CIDR notation.
-	// requestedSubPool indicates a smaller range of addresses from the pool, for now it is specified in CIDR notation.
-	// Both requestedPool and requestedSubPool are non-mandatory fields. When they are not specified, Ipam driver may choose to
-	// return a self chosen pool for this request. In such case the v6 flag needs to be set appropriately so
-	// that the driver would return the expected ip version pool.
-	RequestPool(addressSpace, requestedPool, requestedSubPool string, options map[string]string, v6 bool) (poolID string, pool *net.IPNet, meta map[string]string, err error)
+	// RequestPool allocate an address pool either statically or dynamically
+	// based on req.
+	RequestPool(req PoolRequest) (AllocatedPool, error)
 	// ReleasePool releases the address pool identified by the passed id
 	// ReleasePool releases the address pool identified by the passed id
 	ReleasePool(poolID string) error
 	ReleasePool(poolID string) error
 	// RequestAddress request an address from the specified pool ID. Input options or required IP can be passed.
 	// RequestAddress request an address from the specified pool ID. Input options or required IP can be passed.
@@ -63,6 +60,41 @@ type Ipam interface {
 	IsBuiltIn() bool
 	IsBuiltIn() bool
 }
 }
 
 
+type PoolRequest struct {
+	// AddressSpace is a mandatory field which denotes which block of pools
+	// should be used to make the allocation. This value is opaque, and only
+	// the IPAM driver can interpret it. Each driver might support a different
+	// set of AddressSpace.
+	AddressSpace string
+	// Pool is a prefix in CIDR notation. It's non-mandatory. When specified
+	// the Pool will be statically allocated. The Pool is used for dynamic
+	// address allocation -- except when SubPool is specified.
+	Pool string
+	// SubPool is a subnet from Pool, in CIDR notation too. It's non-mandatory.
+	// When specified, it represents the subnet where addresses will be
+	// dynamically allocated. It can't be specified if Pool isn't specified.
+	SubPool string
+	// Options is a map of opaque k/v passed to the driver. It's non-mandatory.
+	// Drivers are free to ignore it.
+	Options map[string]string
+	// V6 indicates which address family should be used to dynamically allocate
+	// a prefix (ie. when Pool isn't specified).
+	V6 bool
+}
+
+type AllocatedPool struct {
+	// PoolID represents the ID of the allocated pool. Its value is opaque and
+	// shouldn't be interpreted by anything but the IPAM driver that generated
+	// it.
+	PoolID string
+	// Pool is the allocated prefix.
+	Pool netip.Prefix
+	// Meta represents a list of extra IP addresses automatically reserved
+	// during the pool allocation. These are generally keyed by well-known
+	// strings defined in the netlabel package.
+	Meta map[string]string
+}
+
 // Capability represents the requirements and capabilities of the IPAM driver
 // Capability represents the requirements and capabilities of the IPAM driver
 type Capability struct {
 type Capability struct {
 	// Whether on address request, libnetwork must
 	// Whether on address request, libnetwork must

+ 15 - 11
libnetwork/ipams/null/null.go

@@ -4,6 +4,7 @@ package null
 
 
 import (
 import (
 	"net"
 	"net"
+	"net/netip"
 
 
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/types"
 	"github.com/docker/docker/libnetwork/types"
@@ -15,7 +16,7 @@ const (
 	defaultPoolID       = defaultAddressSpace + "/" + defaultPoolCIDR
 	defaultPoolID       = defaultAddressSpace + "/" + defaultPoolCIDR
 )
 )
 
 
-var defaultPool, _ = types.ParseCIDR(defaultPoolCIDR)
+var defaultPool = netip.MustParsePrefix(defaultPoolCIDR)
 
 
 type allocator struct{}
 type allocator struct{}
 
 
@@ -23,20 +24,23 @@ func (a *allocator) GetDefaultAddressSpaces() (string, string, error) {
 	return defaultAddressSpace, defaultAddressSpace, nil
 	return defaultAddressSpace, defaultAddressSpace, nil
 }
 }
 
 
-func (a *allocator) RequestPool(addressSpace, requestedPool, requestedSubPool string, _ map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
-	if addressSpace != defaultAddressSpace {
-		return "", nil, nil, types.InvalidParameterErrorf("unknown address space: %s", addressSpace)
+func (a *allocator) RequestPool(req ipamapi.PoolRequest) (ipamapi.AllocatedPool, error) {
+	if req.AddressSpace != defaultAddressSpace {
+		return ipamapi.AllocatedPool{}, types.InvalidParameterErrorf("unknown address space: %s", req.AddressSpace)
 	}
 	}
-	if requestedPool != "" {
-		return "", nil, nil, types.InvalidParameterErrorf("null ipam driver does not handle specific address pool requests")
+	if req.Pool != "" {
+		return ipamapi.AllocatedPool{}, types.InvalidParameterErrorf("null ipam driver does not handle specific address pool requests")
 	}
 	}
-	if requestedSubPool != "" {
-		return "", nil, nil, types.InvalidParameterErrorf("null ipam driver does not handle specific address subpool requests")
+	if req.SubPool != "" {
+		return ipamapi.AllocatedPool{}, types.InvalidParameterErrorf("null ipam driver does not handle specific address subpool requests")
 	}
 	}
-	if v6 {
-		return "", nil, nil, types.InvalidParameterErrorf("null ipam driver does not handle IPv6 address pool requests")
+	if req.V6 {
+		return ipamapi.AllocatedPool{}, types.InvalidParameterErrorf("null ipam driver does not handle IPv6 address pool requests")
 	}
 	}
-	return defaultPoolID, defaultPool, nil, nil
+	return ipamapi.AllocatedPool{
+		PoolID: defaultPoolID,
+		Pool:   defaultPool,
+	}, nil
 }
 }
 
 
 func (a *allocator) ReleasePool(poolID string) error {
 func (a *allocator) ReleasePool(poolID string) error {

+ 15 - 27
libnetwork/ipams/null/null_test.go

@@ -3,42 +3,30 @@ package null
 import (
 import (
 	"testing"
 	"testing"
 
 
-	"github.com/docker/docker/libnetwork/types"
+	"github.com/docker/docker/libnetwork/ipamapi"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 )
 
 
 func TestPoolRequest(t *testing.T) {
 func TestPoolRequest(t *testing.T) {
 	a := allocator{}
 	a := allocator{}
 
 
-	pid, pool, _, err := a.RequestPool(defaultAddressSpace, "", "", nil, false)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !types.CompareIPNet(defaultPool, pool) {
-		t.Fatalf("Unexpected pool returned. Expected %v. Got: %v", defaultPool, pool)
-	}
-	if pid != defaultPoolID {
-		t.Fatalf("Unexpected pool id returned. Expected: %s. Got: %s", defaultPoolID, pid)
-	}
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: defaultAddressSpace})
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(alloc.PoolID, defaultPoolID))
+	assert.Check(t, is.Equal(alloc.Pool, defaultPool))
 
 
-	_, _, _, err = a.RequestPool("default", "", "", nil, false)
-	if err == nil {
-		t.Fatal("Unexpected success")
-	}
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: "default"})
+	assert.ErrorContains(t, err, "unknown address space: default")
 
 
-	_, _, _, err = a.RequestPool(defaultAddressSpace, "192.168.0.0/16", "", nil, false)
-	if err == nil {
-		t.Fatal("Unexpected success")
-	}
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: defaultAddressSpace, Pool: "192.168.0.0/16"})
+	assert.ErrorContains(t, err, "null ipam driver does not handle specific address pool requests")
 
 
-	_, _, _, err = a.RequestPool(defaultAddressSpace, "", "192.168.0.0/24", nil, false)
-	if err == nil {
-		t.Fatal("Unexpected success")
-	}
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: defaultAddressSpace, SubPool: "192.168.0.0/24"})
+	assert.ErrorContains(t, err, "null ipam driver does not handle specific address subpool requests")
 
 
-	_, _, _, err = a.RequestPool(defaultAddressSpace, "", "", nil, true)
-	if err == nil {
-		t.Fatal("Unexpected success")
-	}
+	_, err = a.RequestPool(ipamapi.PoolRequest{AddressSpace: defaultAddressSpace, V6: true})
+	assert.ErrorContains(t, err, "null ipam driver does not handle IPv6 address pool requests")
 }
 }
 
 
 func TestOtherRequests(t *testing.T) {
 func TestOtherRequests(t *testing.T) {

+ 18 - 6
libnetwork/ipams/remote/remote.go

@@ -4,6 +4,7 @@ import (
 	"context"
 	"context"
 	"fmt"
 	"fmt"
 	"net"
 	"net"
+	"net/netip"
 
 
 	"github.com/containerd/log"
 	"github.com/containerd/log"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/ipamapi"
@@ -116,14 +117,25 @@ func (a *allocator) GetDefaultAddressSpaces() (string, string, error) {
 }
 }
 
 
 // RequestPool requests an address pool in the specified address space
 // RequestPool requests an address pool in the specified address space
-func (a *allocator) RequestPool(addressSpace, requestedPool, requestedSubPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
-	req := &api.RequestPoolRequest{AddressSpace: addressSpace, Pool: requestedPool, SubPool: requestedSubPool, Options: options, V6: v6}
+func (a *allocator) RequestPool(req ipamapi.PoolRequest) (ipamapi.AllocatedPool, error) {
+	remoteReq := &api.RequestPoolRequest{
+		AddressSpace: req.AddressSpace,
+		Pool:         req.Pool,
+		SubPool:      req.SubPool,
+		Options:      req.Options,
+		V6:           req.V6,
+	}
 	res := &api.RequestPoolResponse{}
 	res := &api.RequestPoolResponse{}
-	if err := a.call("RequestPool", req, res); err != nil {
-		return "", nil, nil, err
+	if err := a.call("RequestPool", remoteReq, res); err != nil {
+		return ipamapi.AllocatedPool{}, err
 	}
 	}
-	retPool, err := types.ParseCIDR(res.Pool)
-	return res.PoolID, retPool, res.Data, err
+
+	retPool, err := netip.ParsePrefix(res.Pool)
+	return ipamapi.AllocatedPool{
+		PoolID: res.PoolID,
+		Pool:   retPool,
+		Meta:   res.Data,
+	}, err
 }
 }
 
 
 // ReleasePool removes an address pool from the specified address space
 // ReleasePool removes an address pool from the specified address space

+ 35 - 54
libnetwork/ipams/remote/remote_test.go

@@ -14,6 +14,8 @@ import (
 
 
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/pkg/plugins"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 )
 
 
 func decodeToMap(r *http.Request) (res map[string]interface{}, err error) {
 func decodeToMap(r *http.Request) (res map[string]interface{}, err error) {
@@ -248,72 +250,51 @@ func TestRemoteDriver(t *testing.T) {
 	d := newAllocator(plugin, client)
 	d := newAllocator(plugin, client)
 
 
 	l, g, err := d.(*allocator).GetDefaultAddressSpaces()
 	l, g, err := d.(*allocator).GetDefaultAddressSpaces()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if l != "white" || g != "blue" {
-		t.Fatalf("Unexpected default local/global address spaces: %s, %s", l, g)
-	}
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(l, "white"))
+	assert.Check(t, is.Equal(g, "blue"))
 
 
 	// Request any pool
 	// Request any pool
-	poolID, pool, _, err := d.RequestPool("white", "", "", nil, false)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if poolID != "white/172.18.0.0/16" {
-		t.Fatalf("Unexpected pool id: %s", poolID)
-	}
-	if pool == nil || pool.String() != "172.18.0.0/16" {
-		t.Fatalf("Unexpected pool: %s", pool)
-	}
+	alloc, err := d.RequestPool(ipamapi.PoolRequest{
+		AddressSpace: "white",
+	})
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(alloc.PoolID, "white/172.18.0.0/16"))
+	assert.Check(t, is.Equal(alloc.Pool.String(), "172.18.0.0/16"))
 
 
 	// Request specific pool
 	// Request specific pool
-	poolID2, pool2, ops, err := d.RequestPool("white", "172.20.0.0/16", "", nil, false)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if poolID2 != "white/172.20.0.0/16" {
-		t.Fatalf("Unexpected pool id: %s", poolID2)
-	}
-	if pool2 == nil || pool2.String() != "172.20.0.0/16" {
-		t.Fatalf("Unexpected pool: %s", pool2)
-	}
-	if dns, ok := ops["DNS"]; !ok || dns != "8.8.8.8" {
-		t.Fatal("Missing options")
-	}
+	alloc, err = d.RequestPool(ipamapi.PoolRequest{
+		AddressSpace: "white",
+		Pool:         "172.20.0.0/16",
+	})
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(alloc.PoolID, "white/172.20.0.0/16"))
+	assert.Check(t, is.Equal(alloc.Pool.String(), "172.20.0.0/16"))
+	assert.Check(t, is.Equal(alloc.Meta["DNS"], "8.8.8.8"))
 
 
 	// Request specific pool and subpool
 	// Request specific pool and subpool
-	poolID3, pool3, _, err := d.RequestPool("white", "172.20.0.0/16", "172.20.3.0/24" /*nil*/, map[string]string{"culo": "yes"}, false)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if poolID3 != "white/172.20.0.0/16/172.20.3.0/24" {
-		t.Fatalf("Unexpected pool id: %s", poolID3)
-	}
-	if pool3 == nil || pool3.String() != "172.20.0.0/16" {
-		t.Fatalf("Unexpected pool: %s", pool3)
-	}
+	alloc, err = d.RequestPool(ipamapi.PoolRequest{
+		AddressSpace: "white",
+		Pool:         "172.20.0.0/16",
+		SubPool:      "172.20.3.0/24",
+		Options:      map[string]string{"culo": "yes"},
+	})
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(alloc.PoolID, "white/172.20.0.0/16/172.20.3.0/24"))
+	assert.Check(t, is.Equal(alloc.Pool.String(), "172.20.0.0/16"))
 
 
 	// Request any address
 	// Request any address
-	addr, _, err := d.RequestAddress(poolID2, nil, nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if addr == nil || addr.String() != "172.20.0.34/16" {
-		t.Fatalf("Unexpected address: %s", addr)
-	}
+	addr, _, err := d.RequestAddress("white/172.20.0.0/16", nil, nil)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(addr.String(), "172.20.0.34/16"))
 
 
 	// Request specific address
 	// Request specific address
-	addr2, _, err := d.RequestAddress(poolID2, net.ParseIP("172.20.1.45"), nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if addr2 == nil || addr2.String() != "172.20.1.45/16" {
-		t.Fatalf("Unexpected address: %s", addr2)
-	}
+	addr2, _, err := d.RequestAddress("white/172.20.0.0/16", net.ParseIP("172.20.1.45"), nil)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(addr2.String(), "172.20.1.45/16"))
 
 
 	// Release address
 	// Release address
-	err = d.ReleaseAddress(poolID, net.ParseIP("172.18.1.45"))
+	err = d.ReleaseAddress("white/172.20.0.0/16", net.ParseIP("172.18.1.45"))
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 16 - 15
libnetwork/ipams/windowsipam/windowsipam.go

@@ -2,7 +2,9 @@ package windowsipam
 
 
 import (
 import (
 	"context"
 	"context"
+	"fmt"
 	"net"
 	"net"
+	"net/netip"
 
 
 	"github.com/containerd/log"
 	"github.com/containerd/log"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/ipamapi"
@@ -17,7 +19,7 @@ const (
 // DefaultIPAM defines the default ipam-driver for local-scoped windows networks
 // DefaultIPAM defines the default ipam-driver for local-scoped windows networks
 const DefaultIPAM = "windows"
 const DefaultIPAM = "windows"
 
 
-var defaultPool, _ = types.ParseCIDR("0.0.0.0/0")
+var defaultPool = netip.MustParsePrefix("0.0.0.0/0")
 
 
 type allocator struct{}
 type allocator struct{}
 
 
@@ -32,25 +34,24 @@ func (a *allocator) GetDefaultAddressSpaces() (string, string, error) {
 
 
 // RequestPool returns an address pool along with its unique id. This is a null ipam driver. It allocates the
 // RequestPool returns an address pool along with its unique id. This is a null ipam driver. It allocates the
 // subnet user asked and does not validate anything. Doesn't support subpool allocation
 // subnet user asked and does not validate anything. Doesn't support subpool allocation
-func (a *allocator) RequestPool(addressSpace, requestedPool, requestedSubPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
-	log.G(context.TODO()).Debugf("RequestPool(%s, %s, %s, %v, %t)", addressSpace, requestedPool, requestedSubPool, options, v6)
-	if requestedSubPool != "" || v6 {
-		return "", nil, nil, types.InternalErrorf("This request is not supported by null ipam driver")
+func (a *allocator) RequestPool(req ipamapi.PoolRequest) (ipamapi.AllocatedPool, error) {
+	log.G(context.TODO()).Debugf("RequestPool: %+v", req)
+	if req.SubPool != "" || req.V6 {
+		return ipamapi.AllocatedPool{}, types.InternalErrorf("this request is not supported by the 'windows' ipam driver")
 	}
 	}
 
 
-	var ipNet *net.IPNet
-	var err error
-
-	if requestedPool != "" {
-		_, ipNet, err = net.ParseCIDR(requestedPool)
-		if err != nil {
-			return "", nil, nil, err
+	pool := defaultPool
+	if req.Pool != "" {
+		var err error
+		if pool, err = netip.ParsePrefix(req.Pool); err != nil {
+			return ipamapi.AllocatedPool{}, fmt.Errorf("invalid IPAM request: %w", err)
 		}
 		}
-	} else {
-		ipNet = defaultPool
 	}
 	}
 
 
-	return ipNet.String(), ipNet, nil, nil
+	return ipamapi.AllocatedPool{
+		PoolID: pool.String(),
+		Pool:   pool,
+	}, nil
 }
 }
 
 
 // ReleasePool releases the address pool - always succeeds
 // ReleasePool releases the address pool - always succeeds

+ 27 - 26
libnetwork/ipams/windowsipam/windowsipam_test.go

@@ -7,40 +7,41 @@ import (
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/netlabel"
 	"github.com/docker/docker/libnetwork/netlabel"
 	"github.com/docker/docker/libnetwork/types"
 	"github.com/docker/docker/libnetwork/types"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 )
 
 
 func TestWindowsIPAM(t *testing.T) {
 func TestWindowsIPAM(t *testing.T) {
 	a := &allocator{}
 	a := &allocator{}
-	requestPool, _ := types.ParseCIDR("192.168.0.0/16")
-	requestAddress := net.ParseIP("192.168.1.1")
 
 
-	pid, pool, _, err := a.RequestPool(localAddressSpace, "", "", nil, false)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !types.CompareIPNet(defaultPool, pool) ||
-		pid != pool.String() {
-		t.Fatalf("Unexpected data returned. Expected %v : %s. Got: %v : %s", defaultPool, pid, pool, pool.String())
-	}
+	alloc, err := a.RequestPool(ipamapi.PoolRequest{AddressSpace: localAddressSpace})
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(alloc.PoolID, defaultPool.String()))
+	assert.Check(t, is.Equal(alloc.Pool, defaultPool))
 
 
-	pid, pool, _, err = a.RequestPool(localAddressSpace, requestPool.String(), "", nil, false)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !types.CompareIPNet(requestPool, pool) ||
-		pid != requestPool.String() {
-		t.Fatalf("Unexpected data returned. Expected %v : %s. Got: %v : %s", requestPool, requestPool.String(), pool, pool.String())
-	}
+	alloc, err = a.RequestPool(ipamapi.PoolRequest{
+		AddressSpace: localAddressSpace,
+		Pool:         "192.168.0.0/16",
+	})
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(alloc.PoolID, "192.168.0.0/16"))
+	assert.Check(t, is.Equal(alloc.Pool.String(), "192.168.0.0/16"))
 
 
-	_, _, _, err = a.RequestPool(localAddressSpace, requestPool.String(), requestPool.String(), nil, false)
-	if err == nil {
-		t.Fatal("Unexpected success for subpool request")
-	}
+	_, err = a.RequestPool(ipamapi.PoolRequest{
+		AddressSpace: localAddressSpace,
+		Pool:         "192.168.0.0/16",
+		SubPool:      "192.168.0.0/16",
+	})
+	assert.ErrorContains(t, err, "this request is not supported by the 'windows' ipam driver")
 
 
-	_, _, _, err = a.RequestPool(localAddressSpace, requestPool.String(), "", nil, true)
-	if err == nil {
-		t.Fatal("Unexpected success for v6 request")
-	}
+	_, err = a.RequestPool(ipamapi.PoolRequest{
+		AddressSpace: localAddressSpace,
+		V6:           true,
+	})
+	assert.ErrorContains(t, err, "this request is not supported by the 'windows' ipam driver")
+
+	requestPool, _ := types.ParseCIDR("192.168.0.0/16")
+	requestAddress := net.ParseIP("192.168.1.1")
 
 
 	err = a.ReleasePool(requestPool.String())
 	err = a.ReleasePool(requestPool.String())
 	if err != nil {
 	if err != nil {

+ 15 - 8
libnetwork/network.go

@@ -17,6 +17,7 @@ import (
 	"github.com/docker/docker/libnetwork/datastore"
 	"github.com/docker/docker/libnetwork/datastore"
 	"github.com/docker/docker/libnetwork/driverapi"
 	"github.com/docker/docker/libnetwork/driverapi"
 	"github.com/docker/docker/libnetwork/etchosts"
 	"github.com/docker/docker/libnetwork/etchosts"
+	"github.com/docker/docker/libnetwork/internal/netiputil"
 	"github.com/docker/docker/libnetwork/internal/setmatrix"
 	"github.com/docker/docker/libnetwork/internal/setmatrix"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/ipamapi"
 	"github.com/docker/docker/libnetwork/netlabel"
 	"github.com/docker/docker/libnetwork/netlabel"
@@ -1508,7 +1509,7 @@ func (n *Network) ipamAllocate() error {
 	return err
 	return err
 }
 }
 
 
-func (n *Network) requestPoolHelper(ipam ipamapi.Ipam, addressSpace, requestedPool, requestedSubPool string, options map[string]string, v6 bool) (poolID string, pool *net.IPNet, meta map[string]string, err error) {
+func (n *Network) requestPoolHelper(ipam ipamapi.Ipam, addressSpace, requestedPool, requestedSubPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
 	var tmpPoolLeases []string
 	var tmpPoolLeases []string
 	defer func() {
 	defer func() {
 		// Prevent repeated lock/unlock in the loop.
 		// Prevent repeated lock/unlock in the loop.
@@ -1516,13 +1517,19 @@ func (n *Network) requestPoolHelper(ipam ipamapi.Ipam, addressSpace, requestedPo
 		// Release all pools we held on to.
 		// Release all pools we held on to.
 		for _, pID := range tmpPoolLeases {
 		for _, pID := range tmpPoolLeases {
 			if err := ipam.ReleasePool(pID); err != nil {
 			if err := ipam.ReleasePool(pID); err != nil {
-				log.G(context.TODO()).Warnf("Failed to release overlapping pool %s while returning from pool request helper for network %s", pool, nwName)
+				log.G(context.TODO()).Warnf("Failed to release overlapping pool while returning from pool request helper for network %s", nwName)
 			}
 			}
 		}
 		}
 	}()
 	}()
 
 
 	for {
 	for {
-		poolID, pool, meta, err = ipam.RequestPool(addressSpace, requestedPool, requestedSubPool, options, v6)
+		alloc, err := ipam.RequestPool(ipamapi.PoolRequest{
+			AddressSpace: addressSpace,
+			Pool:         requestedPool,
+			SubPool:      requestedSubPool,
+			Options:      options,
+			V6:           v6,
+		})
 		if err != nil {
 		if err != nil {
 			return "", nil, nil, err
 			return "", nil, nil, err
 		}
 		}
@@ -1544,13 +1551,13 @@ func (n *Network) requestPoolHelper(ipam ipamapi.Ipam, addressSpace, requestedPo
 		// instead would likely have avoided that as well, so we can only guess.
 		// instead would likely have avoided that as well, so we can only guess.
 		//
 		//
 		// [1]: https://github.com/moby/libnetwork/commit/5ca79d6b87873264516323a7b76f0af7d0298492#diff-bdcd879439d041827d334846f9aba01de6e3683ed8fdd01e63917dae6df23846
 		// [1]: https://github.com/moby/libnetwork/commit/5ca79d6b87873264516323a7b76f0af7d0298492#diff-bdcd879439d041827d334846f9aba01de6e3683ed8fdd01e63917dae6df23846
-		if requestedPool != "" || n.Scope() == scope.Global || pool.String() == "0.0.0.0/0" {
-			return poolID, pool, meta, nil
+		if requestedPool != "" || n.Scope() == scope.Global || alloc.Pool.String() == "0.0.0.0/0" {
+			return alloc.PoolID, netiputil.ToIPNet(alloc.Pool), alloc.Meta, nil
 		}
 		}
 
 
 		// Check for overlap and if none found, we have found the right pool.
 		// Check for overlap and if none found, we have found the right pool.
-		if _, err := netutils.FindAvailableNetwork([]*net.IPNet{pool}); err == nil {
-			return poolID, pool, meta, nil
+		if _, err := netutils.FindAvailableNetwork([]*net.IPNet{netiputil.ToIPNet(alloc.Pool)}); err == nil {
+			return alloc.PoolID, netiputil.ToIPNet(alloc.Pool), alloc.Meta, nil
 		}
 		}
 
 
 		// Pool obtained in this iteration is overlapping. Hold onto the pool
 		// Pool obtained in this iteration is overlapping. Hold onto the pool
@@ -1558,7 +1565,7 @@ func (n *Network) requestPoolHelper(ipam ipamapi.Ipam, addressSpace, requestedPo
 		// the same pool over again. But make sure we still do a deferred release
 		// the same pool over again. But make sure we still do a deferred release
 		// when we have either obtained a non-overlapping pool or ran out of
 		// when we have either obtained a non-overlapping pool or ran out of
 		// pre-defined pools.
 		// pre-defined pools.
-		tmpPoolLeases = append(tmpPoolLeases, poolID)
+		tmpPoolLeases = append(tmpPoolLeases, alloc.PoolID)
 	}
 	}
 }
 }