Преглед на файлове

libnet/ipam: drop vestiges of custom addrSpaces

Only two address spaces are supported: LocalDefault and GlobalDefault.
Support for non-default address spaces in the IPAM Allocator is
vestigial, from a time when IPAM state was stored in a persistent shared
datastore. There is no way to create non-default address spaces through
the IPAM API so there is no need to retain code to support the use of
such address spaces. Drop all pretense that more address spaces can
exist, to the extent that the IPAM API allows.

Signed-off-by: Cory Snider <csnider@mirantis.com>
Cory Snider преди 2 години
родител
ревизия
9a8b45c133
променени са 4 файла, в които са добавени 65 реда и са изтрити 115 реда
  1. 38 57
      libnetwork/ipam/allocator.go
  2. 21 52
      libnetwork/ipam/allocator_test.go
  3. 1 6
      libnetwork/ipam/parallel_test.go
  4. 5 0
      libnetwork/ipam/structures.go

+ 38 - 57
libnetwork/ipam/allocator.go

@@ -3,7 +3,6 @@ package ipam
 import (
 import (
 	"fmt"
 	"fmt"
 	"net"
 	"net"
-	"sort"
 	"sync"
 	"sync"
 
 
 	"github.com/docker/docker/libnetwork/bitseq"
 	"github.com/docker/docker/libnetwork/bitseq"
@@ -19,12 +18,8 @@ const (
 
 
 // Allocator provides per address space ipv4/ipv6 book keeping
 // Allocator provides per address space ipv4/ipv6 book keeping
 type Allocator struct {
 type Allocator struct {
-	// Predefined pools for default address spaces
-	// Separate from the addrSpace because they should not be serialized
-	predefined             map[string][]*net.IPNet
-	predefinedStartIndices map[string]int
 	// The address spaces
 	// The address spaces
-	addrSpaces map[string]*addrSpace
+	local, global *addrSpace
 	// Allocated addresses in each address space's subnet
 	// Allocated addresses in each address space's subnet
 	addresses map[SubnetKey]*bitseq.Handle
 	addresses map[SubnetKey]*bitseq.Handle
 	sync.Mutex
 	sync.Mutex
@@ -33,26 +28,20 @@ type Allocator struct {
 // NewAllocator returns an instance of libnetwork ipam
 // NewAllocator returns an instance of libnetwork ipam
 func NewAllocator(lcAs, glAs []*net.IPNet) (*Allocator, error) {
 func NewAllocator(lcAs, glAs []*net.IPNet) (*Allocator, error) {
 	a := &Allocator{
 	a := &Allocator{
-		predefined: map[string][]*net.IPNet{
-			localAddressSpace:  lcAs,
-			globalAddressSpace: glAs,
-		},
-		predefinedStartIndices: map[string]int{},
-		addresses:              map[SubnetKey]*bitseq.Handle{},
+		addresses: map[SubnetKey]*bitseq.Handle{},
 	}
 	}
 
 
-	a.addrSpaces = map[string]*addrSpace{
-		localAddressSpace:  a.newAddressSpace(),
-		globalAddressSpace: a.newAddressSpace(),
-	}
+	a.local = newAddrSpace(a, lcAs)
+	a.global = newAddrSpace(a, glAs)
 
 
 	return a, nil
 	return a, nil
 }
 }
 
 
-func (a *Allocator) newAddressSpace() *addrSpace {
+func newAddrSpace(a *Allocator, predefined []*net.IPNet) *addrSpace {
 	return &addrSpace{
 	return &addrSpace{
-		subnets: map[SubnetKey]*PoolData{},
-		alloc:   a,
+		subnets:    map[SubnetKey]*PoolData{},
+		alloc:      a,
+		predefined: predefined,
 	}
 	}
 }
 }
 
 
@@ -121,11 +110,13 @@ func (a *Allocator) ReleasePool(poolID string) error {
 func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) {
 func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) {
 	a.Lock()
 	a.Lock()
 	defer a.Unlock()
 	defer a.Unlock()
-	aSpace, ok := a.addrSpaces[as]
-	if !ok {
-		return nil, types.BadRequestErrorf("cannot find address space %s", as)
+	switch as {
+	case localAddressSpace:
+		return a.local, nil
+	case globalAddressSpace:
+		return a.global, nil
 	}
 	}
-	return aSpace, nil
+	return nil, types.BadRequestErrorf("cannot find address space %s", as)
 }
 }
 
 
 // parsePoolRequest parses and validates a request to create a new pool under addressSpace and returns
 // parsePoolRequest parses and validates a request to create a new pool under addressSpace and returns
@@ -214,27 +205,27 @@ func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle,
 	return bm, nil
 	return bm, nil
 }
 }
 
 
-func (a *Allocator) getPredefineds(as string) []*net.IPNet {
-	a.Lock()
-	defer a.Unlock()
-
-	p := a.predefined[as]
-	i := a.predefinedStartIndices[as]
+// getPredefineds returns the predefined subnets for the address space.
+//
+// It should not be called concurrently with any other method on the addrSpace.
+func (aSpace *addrSpace) getPredefineds() []*net.IPNet {
+	i := aSpace.predefinedStartIndex
 	// defensive in case the list changed since last update
 	// defensive in case the list changed since last update
-	if i >= len(p) {
+	if i >= len(aSpace.predefined) {
 		i = 0
 		i = 0
 	}
 	}
-	return append(p[i:], p[:i]...)
+	return append(aSpace.predefined[i:], aSpace.predefined[:i]...)
 }
 }
 
 
-func (a *Allocator) updateStartIndex(as string, amt int) {
-	a.Lock()
-	i := a.predefinedStartIndices[as] + amt
-	if i < 0 || i >= len(a.predefined[as]) {
+// updatePredefinedStartIndex rotates the predefined subnet list by amt.
+//
+// It should not be called concurrently with any other method on the addrSpace.
+func (aSpace *addrSpace) updatePredefinedStartIndex(amt int) {
+	i := aSpace.predefinedStartIndex + amt
+	if i < 0 || i >= len(aSpace.predefined) {
 		i = 0
 		i = 0
 	}
 	}
-	a.predefinedStartIndices[as] = i
-	a.Unlock()
+	aSpace.predefinedStartIndex = i
 }
 }
 
 
 func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) {
 func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) {
@@ -244,19 +235,15 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
 		v = v6
 		v = v6
 	}
 	}
 
 
-	if as != localAddressSpace && as != globalAddressSpace {
+	aSpace, _ := a.getAddrSpace(as)
+	if aSpace == nil {
 		return nil, types.NotImplementedErrorf("no default pool available for non-default address spaces")
 		return nil, types.NotImplementedErrorf("no default pool available for non-default address spaces")
 	}
 	}
 
 
-	aSpace, err := a.getAddrSpace(as)
-	if err != nil {
-		return nil, err
-	}
-
-	predefined := a.getPredefineds(as)
-
 	aSpace.Lock()
 	aSpace.Lock()
-	for i, nw := range predefined {
+	defer aSpace.Unlock()
+
+	for i, nw := range aSpace.getPredefineds() {
 		if v != getAddressVersion(nw.IP) {
 		if v != getAddressVersion(nw.IP) {
 			continue
 			continue
 		}
 		}
@@ -267,12 +254,10 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
 		// Shouldn't be necessary, but check prevents IP collisions should
 		// Shouldn't be necessary, but check prevents IP collisions should
 		// predefined pools overlap for any reason.
 		// predefined pools overlap for any reason.
 		if !aSpace.contains(as, nw) {
 		if !aSpace.contains(as, nw) {
-			aSpace.Unlock()
-			a.updateStartIndex(as, i+1)
+			aSpace.updatePredefinedStartIndex(i + 1)
 			return nw, nil
 			return nw, nil
 		}
 		}
 	}
 	}
-	aSpace.Unlock()
 
 
 	return nil, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v)
 	return nil, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v)
 }
 }
@@ -426,18 +411,14 @@ func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddres
 // DumpDatabase dumps the internal info
 // DumpDatabase dumps the internal info
 func (a *Allocator) DumpDatabase() string {
 func (a *Allocator) DumpDatabase() string {
 	a.Lock()
 	a.Lock()
-	aspaces := make(map[string]*addrSpace, len(a.addrSpaces))
-	orderedAS := make([]string, 0, len(a.addrSpaces))
-	for as, aSpace := range a.addrSpaces {
-		orderedAS = append(orderedAS, as)
-		aspaces[as] = aSpace
+	aspaces := map[string]*addrSpace{
+		localAddressSpace:  a.local,
+		globalAddressSpace: a.global,
 	}
 	}
 	a.Unlock()
 	a.Unlock()
 
 
-	sort.Strings(orderedAS)
-
 	var s string
 	var s string
-	for _, as := range orderedAS {
+	for _, as := range []string{localAddressSpace, globalAddressSpace} {
 		aSpace := aspaces[as]
 		aSpace := aspaces[as]
 		s = fmt.Sprintf("\n\n%s Config", as)
 		s = fmt.Sprintf("\n\n%s Config", as)
 		aSpace.Lock()
 		aSpace.Lock()

+ 21 - 52
libnetwork/ipam/allocator_test.go

@@ -83,14 +83,13 @@ func TestAddSubnets(t *testing.T) {
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	a.addrSpaces["abc"] = a.addrSpaces[localAddressSpace]
 
 
 	pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false)
 	pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false)
 	if err != nil {
 	if err != nil {
 		t.Fatal("Unexpected failure in adding subnet")
 		t.Fatal("Unexpected failure in adding subnet")
 	}
 	}
 
 
-	pid1, _, _, err := a.RequestPool("abc", "10.0.0.0/8", "", nil, false)
+	pid1, _, _, err := a.RequestPool(globalAddressSpace, "10.0.0.0/8", "", nil, false)
 	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)
 	}
 	}
@@ -99,21 +98,21 @@ func TestAddSubnets(t *testing.T) {
 		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("abc", "10.0.0.0/8", "", nil, false)
+	_, _, _, err = a.RequestPool(globalAddressSpace, "10.0.0.0/8", "", nil, false)
 	if err == nil {
 	if err == nil {
 		t.Fatalf("Expected failure requesting existing subnet")
 		t.Fatalf("Expected failure requesting existing subnet")
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool("abc", "10.128.0.0/9", "", nil, false)
+	_, _, _, err = a.RequestPool(globalAddressSpace, "10.128.0.0/9", "", nil, false)
 	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("abc", "10.0.0.0/8", "10.128.0.0/9", nil, false)
+	_, _, _, err = a.RequestPool(globalAddressSpace, "10.0.0.0/8", "10.128.0.0/9", nil, false)
 	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("abc", "10.0.0.0/8", "10.128.0.0/9", nil, false)
+	_, _, _, err = a.RequestPool(globalAddressSpace, "10.0.0.0/8", "10.128.0.0/9", nil, false)
 	if err == nil {
 	if err == nil {
 		t.Fatalf("Expected failure on adding overlapping sub pool")
 		t.Fatalf("Expected failure on adding overlapping sub pool")
 	}
 	}
@@ -317,11 +316,6 @@ 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)
 
 
-	a.addrSpaces["splane"] = &addrSpace{
-		alloc:   a.addrSpaces[localAddressSpace].alloc,
-		subnets: map[SubnetKey]*PoolData{},
-	}
-
 	input := []struct {
 	input := []struct {
 		addrSpace string
 		addrSpace string
 		subnet    string
 		subnet    string
@@ -331,10 +325,10 @@ func TestRemoveSubnet(t *testing.T) {
 		{localAddressSpace, "172.17.0.0/16", false},
 		{localAddressSpace, "172.17.0.0/16", false},
 		{localAddressSpace, "10.0.0.0/8", false},
 		{localAddressSpace, "10.0.0.0/8", false},
 		{localAddressSpace, "2001:db8:1:2:3:4:ffff::/112", false},
 		{localAddressSpace, "2001:db8:1:2:3:4:ffff::/112", false},
-		{"splane", "172.17.0.0/16", false},
-		{"splane", "10.0.0.0/8", false},
-		{"splane", "2001:db8:1:2:3:4:5::/112", true},
-		{"splane", "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},
 	}
 	}
 
 
 	poolIDs := make([]string, len(input))
 	poolIDs := make([]string, len(input))
@@ -356,12 +350,7 @@ 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)
 
 
-	a.addrSpaces["giallo"] = &addrSpace{
-		alloc:   a.addrSpaces[localAddressSpace].alloc,
-		subnets: map[SubnetKey]*PoolData{},
-	}
-
-	pid, _, _, err := a.RequestPool("giallo", "192.168.100.0/24", "", nil, false)
+	pid, _, _, err := a.RequestPool(localAddressSpace, "192.168.100.0/24", "", nil, false)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -435,12 +424,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)
 
 
-	a.addrSpaces["rosso"] = &addrSpace{
-		alloc:   a.addrSpaces[localAddressSpace].alloc,
-		subnets: map[SubnetKey]*PoolData{},
-	}
-
-	poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false)
+	poolID, _, _, err := a.RequestPool(localAddressSpace, "172.28.0.0/16", "172.28.30.0/24", nil, false)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -470,11 +454,11 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 		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("rosso", "10.0.0.0/8", "10.0.0.0/16", nil, false)
+	_, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	poolID, _, _, err = a.RequestPool("rosso", "10.0.0.0/16", "10.0.0.0/24", nil, false)
+	poolID, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/16", "10.0.0.0/24", nil, false)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -507,7 +491,7 @@ 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("rosso", "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil {
+	if poolID, _, _, err = a.RequestPool(localAddressSpace, "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	tre, _, err := a.RequestAddress(poolID, treExp.IP, nil)
 	tre, _, err := a.RequestAddress(poolID, treExp.IP, nil)
@@ -563,12 +547,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)
 
 
-	a.addrSpaces["rosso"] = &addrSpace{
-		alloc:   a.addrSpaces[localAddressSpace].alloc,
-		subnets: map[SubnetKey]*PoolData{},
-	}
-
-	poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false)
+	poolID, _, _, err := a.RequestPool(localAddressSpace, "172.28.0.0/16", "172.28.30.0/24", nil, false)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -598,11 +577,11 @@ func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 		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("rosso", "10.0.0.0/8", "10.0.0.0/16", nil, false)
+	_, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	poolID, _, _, err = a.RequestPool("rosso", "10.0.0.0/16", "10.0.0.0/24", nil, false)
+	poolID, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/16", "10.0.0.0/24", nil, false)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -636,7 +615,7 @@ 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("rosso", "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil {
+	if poolID, _, _, err = a.RequestPool(localAddressSpace, "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	tre, _, err := a.RequestAddress(poolID, treExp.IP, opts)
 	tre, _, err := a.RequestAddress(poolID, treExp.IP, opts)
@@ -702,17 +681,11 @@ func TestRequestSyntaxCheck(t *testing.T) {
 	var (
 	var (
 		pool    = "192.168.0.0/16"
 		pool    = "192.168.0.0/16"
 		subPool = "192.168.0.0/24"
 		subPool = "192.168.0.0/24"
-		as      = "green"
 	)
 	)
 
 
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks())
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 
 
-	a.addrSpaces[as] = &addrSpace{
-		alloc:   a.addrSpaces[localAddressSpace].alloc,
-		subnets: map[SubnetKey]*PoolData{},
-	}
-
 	_, _, _, err = a.RequestPool("", pool, "", nil, false)
 	_, _, _, err = a.RequestPool("", pool, "", nil, false)
 	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")
@@ -723,12 +696,12 @@ func TestRequestSyntaxCheck(t *testing.T) {
 		t.Fatal("Failed to detect wrong request: empty address space")
 		t.Fatal("Failed to detect wrong request: empty address space")
 	}
 	}
 
 
-	_, _, _, err = a.RequestPool(as, "", subPool, nil, false)
+	_, _, _, err = a.RequestPool(localAddressSpace, "", subPool, nil, false)
 	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(as, pool, subPool, nil, false)
+	pid, _, _, err := a.RequestPool(localAddressSpace, pool, subPool, nil, false)
 	if err != nil {
 	if err != nil {
 		t.Fatalf("Unexpected failure: %v", err)
 		t.Fatalf("Unexpected failure: %v", err)
 	}
 	}
@@ -1234,17 +1207,13 @@ func TestRequestReleaseAddressDuplicate(t *testing.T) {
 	}
 	}
 	ips := []IP{}
 	ips := []IP{}
 	allocatedIPs := []*net.IPNet{}
 	allocatedIPs := []*net.IPNet{}
-	a.addrSpaces["rosso"] = &addrSpace{
-		alloc:   a.addrSpaces[localAddressSpace].alloc,
-		subnets: map[SubnetKey]*PoolData{},
-	}
 
 
 	opts := map[string]string{
 	opts := map[string]string{
 		ipamapi.AllocSerialPrefix: "true",
 		ipamapi.AllocSerialPrefix: "true",
 	}
 	}
 	var l sync.Mutex
 	var l sync.Mutex
 
 
-	poolID, _, _, err := a.RequestPool("rosso", "198.168.0.0/23", "", nil, false)
+	poolID, _, _, err := a.RequestPool(localAddressSpace, "198.168.0.0/23", "", nil, false)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 1 - 6
libnetwork/ipam/parallel_test.go

@@ -41,16 +41,11 @@ func newTestContext(t *testing.T, mask int, options map[string]string) *testCont
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
-	a.addrSpaces["giallo"] = &addrSpace{
-		alloc:   a.addrSpaces[localAddressSpace].alloc,
-		subnets: map[SubnetKey]*PoolData{},
-	}
-
 	network := fmt.Sprintf("192.168.100.0/%d", mask)
 	network := fmt.Sprintf("192.168.100.0/%d", mask)
 	// 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("giallo", network, "", nil, false)
+	pid, _, _, err := a.RequestPool(localAddressSpace, network, "", nil, false)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}

+ 5 - 0
libnetwork/ipam/structures.go

@@ -29,6 +29,11 @@ type PoolData struct {
 type addrSpace struct {
 type addrSpace struct {
 	subnets map[SubnetKey]*PoolData
 	subnets map[SubnetKey]*PoolData
 	alloc   *Allocator
 	alloc   *Allocator
+
+	// Predefined pool for the address space
+	predefined           []*net.IPNet
+	predefinedStartIndex int
+
 	sync.Mutex
 	sync.Mutex
 }
 }