Browse code

Change ip allocation logic

Now IP reuses only after all IPs from network was allocated
Fixes #5729

Docker-DCO-1.1-Signed-off-by: Alexandr Morozov <lk4d4math@gmail.com> (github: LK4D4)

Alexandr Morozov authored on 2014/05/14 04:04:45
Showing 2 changed files
... ...
@@ -7,9 +7,19 @@ import (
7 7
 	"github.com/dotcloud/docker/pkg/collections"
8 8
 	"net"
9 9
 	"sync"
10
+	"sync/atomic"
10 11
 )
11 12
 
12
-type networkSet map[string]*collections.OrderedIntSet
13
+type allocatedMap struct {
14
+	*collections.OrderedIntSet
15
+	last int32
16
+}
17
+
18
+func newAllocatedMap() *allocatedMap {
19
+	return &allocatedMap{OrderedIntSet: collections.NewOrderedIntSet()}
20
+}
21
+
22
+type networkSet map[string]*allocatedMap
13 23
 
14 24
 var (
15 25
 	ErrNoAvailableIPs     = errors.New("no available ip addresses on network")
... ...
@@ -19,7 +29,6 @@ var (
19 19
 var (
20 20
 	lock         = sync.Mutex{}
21 21
 	allocatedIPs = networkSet{}
22
-	availableIPS = networkSet{}
23 22
 )
24 23
 
25 24
 // RequestIP requests an available ip from the given network.  It
... ...
@@ -55,13 +64,11 @@ func ReleaseIP(address *net.IPNet, ip *net.IP) error {
55 55
 	checkAddress(address)
56 56
 
57 57
 	var (
58
-		existing  = allocatedIPs[address.String()]
59
-		available = availableIPS[address.String()]
58
+		allocated = allocatedIPs[address.String()]
60 59
 		pos       = getPosition(address, ip)
61 60
 	)
62 61
 
63
-	existing.Remove(int(pos))
64
-	available.Push(int(pos))
62
+	allocated.Remove(int(pos))
65 63
 
66 64
 	return nil
67 65
 }
... ...
@@ -82,29 +89,19 @@ func getPosition(address *net.IPNet, ip *net.IP) int32 {
82 82
 func getNextIp(address *net.IPNet) (*net.IP, error) {
83 83
 	var (
84 84
 		ownIP     = ipToInt(&address.IP)
85
-		available = availableIPS[address.String()]
86 85
 		allocated = allocatedIPs[address.String()]
87 86
 		first, _  = networkdriver.NetworkRange(address)
88 87
 		base      = ipToInt(&first)
89 88
 		size      = int(networkdriver.NetworkSize(address.Mask))
90 89
 		max       = int32(size - 2) // size -1 for the broadcast address, -1 for the gateway address
91
-		pos       = int32(available.Pop())
90
+		pos       = atomic.LoadInt32(&allocated.last)
92 91
 	)
93 92
 
94
-	// We pop and push the position not the ip
95
-	if pos != 0 {
96
-		ip := intToIP(int32(base + pos))
97
-		allocated.Push(int(pos))
98
-
99
-		return ip, nil
100
-	}
101
-
102 93
 	var (
103 94
 		firstNetIP = address.IP.To4().Mask(address.Mask)
104 95
 		firstAsInt = ipToInt(&firstNetIP) + 1
105 96
 	)
106 97
 
107
-	pos = int32(allocated.PullBack())
108 98
 	for i := int32(0); i < max; i++ {
109 99
 		pos = pos%max + 1
110 100
 		next := int32(base + pos)
... ...
@@ -116,6 +113,7 @@ func getNextIp(address *net.IPNet) (*net.IP, error) {
116 116
 		if !allocated.Exists(int(pos)) {
117 117
 			ip := intToIP(next)
118 118
 			allocated.Push(int(pos))
119
+			atomic.StoreInt32(&allocated.last, pos)
119 120
 			return ip, nil
120 121
 		}
121 122
 	}
... ...
@@ -124,15 +122,14 @@ func getNextIp(address *net.IPNet) (*net.IP, error) {
124 124
 
125 125
 func registerIP(address *net.IPNet, ip *net.IP) error {
126 126
 	var (
127
-		existing  = allocatedIPs[address.String()]
128
-		available = availableIPS[address.String()]
127
+		allocated = allocatedIPs[address.String()]
129 128
 		pos       = getPosition(address, ip)
130 129
 	)
131 130
 
132
-	if existing.Exists(int(pos)) {
131
+	if allocated.Exists(int(pos)) {
133 132
 		return ErrIPAlreadyAllocated
134 133
 	}
135
-	available.Remove(int(pos))
134
+	atomic.StoreInt32(&allocated.last, pos)
136 135
 
137 136
 	return nil
138 137
 }
... ...
@@ -153,7 +150,6 @@ func intToIP(n int32) *net.IP {
153 153
 func checkAddress(address *net.IPNet) {
154 154
 	key := address.String()
155 155
 	if _, exists := allocatedIPs[key]; !exists {
156
-		allocatedIPs[key] = collections.NewOrderedIntSet()
157
-		availableIPS[key] = collections.NewOrderedIntSet()
156
+		allocatedIPs[key] = newAllocatedMap()
158 157
 	}
159 158
 }
... ...
@@ -8,7 +8,6 @@ import (
8 8
 
9 9
 func reset() {
10 10
 	allocatedIPs = networkSet{}
11
-	availableIPS = networkSet{}
12 11
 }
13 12
 
14 13
 func TestRequestNewIps(t *testing.T) {
... ...
@@ -18,8 +17,10 @@ func TestRequestNewIps(t *testing.T) {
18 18
 		Mask: []byte{255, 255, 255, 0},
19 19
 	}
20 20
 
21
+	var ip *net.IP
22
+	var err error
21 23
 	for i := 2; i < 10; i++ {
22
-		ip, err := RequestIP(network, nil)
24
+		ip, err = RequestIP(network, nil)
23 25
 		if err != nil {
24 26
 			t.Fatal(err)
25 27
 		}
... ...
@@ -28,6 +29,17 @@ func TestRequestNewIps(t *testing.T) {
28 28
 			t.Fatalf("Expected ip %s got %s", expected, ip.String())
29 29
 		}
30 30
 	}
31
+	value := intToIP(ipToInt(ip) + 1).String()
32
+	if err := ReleaseIP(network, ip); err != nil {
33
+		t.Fatal(err)
34
+	}
35
+	ip, err = RequestIP(network, nil)
36
+	if err != nil {
37
+		t.Fatal(err)
38
+	}
39
+	if ip.String() != value {
40
+		t.Fatalf("Expected to receive the next ip %s got %s", value, ip.String())
41
+	}
31 42
 }
32 43
 
33 44
 func TestReleaseIp(t *testing.T) {
... ...
@@ -64,6 +76,17 @@ func TestGetReleasedIp(t *testing.T) {
64 64
 		t.Fatal(err)
65 65
 	}
66 66
 
67
+	for i := 0; i < 252; i++ {
68
+		_, err = RequestIP(network, nil)
69
+		if err != nil {
70
+			t.Fatal(err)
71
+		}
72
+		err = ReleaseIP(network, ip)
73
+		if err != nil {
74
+			t.Fatal(err)
75
+		}
76
+	}
77
+
67 78
 	ip, err = RequestIP(network, nil)
68 79
 	if err != nil {
69 80
 		t.Fatal(err)
... ...
@@ -185,24 +208,6 @@ func TestIPAllocator(t *testing.T) {
185 185
 
186 186
 		newIPs[i] = ip
187 187
 	}
188
-	// Before loop begin
189
-	// 2(u) - 3(u) - 4(f) - 5(f) - 6(f)
190
-	//                       ↑
191
-
192
-	// After i = 0
193
-	// 2(u) - 3(u) - 4(f) - 5(u) - 6(f)
194
-	//                              ↑
195
-
196
-	// After i = 1
197
-	// 2(u) - 3(u) - 4(f) - 5(u) - 6(u)
198
-	//                ↑
199
-
200
-	// After i = 2
201
-	// 2(u) - 3(u) - 4(u) - 5(u) - 6(u)
202
-	//                       ↑
203
-
204
-	// Reordered these because the new set will always return the
205
-	// lowest ips first and not in the order that they were released
206 188
 	assertIPEquals(t, &expectedIPs[2], newIPs[0])
207 189
 	assertIPEquals(t, &expectedIPs[3], newIPs[1])
208 190
 	assertIPEquals(t, &expectedIPs[4], newIPs[2])
... ...
@@ -234,6 +239,86 @@ func TestAllocateFirstIP(t *testing.T) {
234 234
 	}
235 235
 }
236 236
 
237
+func TestAllocateAllIps(t *testing.T) {
238
+	defer reset()
239
+	network := &net.IPNet{
240
+		IP:   []byte{192, 168, 0, 1},
241
+		Mask: []byte{255, 255, 255, 0},
242
+	}
243
+
244
+	var (
245
+		current, first *net.IP
246
+		err            error
247
+		isFirst        = true
248
+	)
249
+
250
+	for err == nil {
251
+		current, err = RequestIP(network, nil)
252
+		if isFirst {
253
+			first = current
254
+			isFirst = false
255
+		}
256
+	}
257
+
258
+	if err != ErrNoAvailableIPs {
259
+		t.Fatal(err)
260
+	}
261
+
262
+	if _, err := RequestIP(network, nil); err != ErrNoAvailableIPs {
263
+		t.Fatal(err)
264
+	}
265
+
266
+	if err := ReleaseIP(network, first); err != nil {
267
+		t.Fatal(err)
268
+	}
269
+
270
+	again, err := RequestIP(network, nil)
271
+	if err != nil {
272
+		t.Fatal(err)
273
+	}
274
+
275
+	assertIPEquals(t, first, again)
276
+}
277
+
278
+func TestAllocateDifferentSubnets(t *testing.T) {
279
+	defer reset()
280
+	network1 := &net.IPNet{
281
+		IP:   []byte{192, 168, 0, 1},
282
+		Mask: []byte{255, 255, 255, 0},
283
+	}
284
+	network2 := &net.IPNet{
285
+		IP:   []byte{127, 0, 0, 1},
286
+		Mask: []byte{255, 255, 255, 0},
287
+	}
288
+	expectedIPs := []net.IP{
289
+		0: net.IPv4(192, 168, 0, 2),
290
+		1: net.IPv4(192, 168, 0, 3),
291
+		2: net.IPv4(127, 0, 0, 2),
292
+		3: net.IPv4(127, 0, 0, 3),
293
+	}
294
+
295
+	ip11, err := RequestIP(network1, nil)
296
+	if err != nil {
297
+		t.Fatal(err)
298
+	}
299
+	ip12, err := RequestIP(network1, nil)
300
+	if err != nil {
301
+		t.Fatal(err)
302
+	}
303
+	ip21, err := RequestIP(network2, nil)
304
+	if err != nil {
305
+		t.Fatal(err)
306
+	}
307
+	ip22, err := RequestIP(network2, nil)
308
+	if err != nil {
309
+		t.Fatal(err)
310
+	}
311
+	assertIPEquals(t, &expectedIPs[0], ip11)
312
+	assertIPEquals(t, &expectedIPs[1], ip12)
313
+	assertIPEquals(t, &expectedIPs[2], ip21)
314
+	assertIPEquals(t, &expectedIPs[3], ip22)
315
+}
316
+
237 317
 func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) {
238 318
 	if !ip1.Equal(*ip2) {
239 319
 		t.Fatalf("Expected IP %s, got %s", ip1, ip2)