1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

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)
This commit is contained in:
Alexandr Morozov 2014-05-13 23:04:45 +04:00
parent 5128feb690
commit 80fca061e7
2 changed files with 124 additions and 43 deletions

View file

@ -7,9 +7,19 @@ import (
"github.com/dotcloud/docker/pkg/collections"
"net"
"sync"
"sync/atomic"
)
type networkSet map[string]*collections.OrderedIntSet
type allocatedMap struct {
*collections.OrderedIntSet
last int32
}
func newAllocatedMap() *allocatedMap {
return &allocatedMap{OrderedIntSet: collections.NewOrderedIntSet()}
}
type networkSet map[string]*allocatedMap
var (
ErrNoAvailableIPs = errors.New("no available ip addresses on network")
@ -19,7 +29,6 @@ var (
var (
lock = sync.Mutex{}
allocatedIPs = networkSet{}
availableIPS = networkSet{}
)
// RequestIP requests an available ip from the given network. It
@ -55,13 +64,11 @@ func ReleaseIP(address *net.IPNet, ip *net.IP) error {
checkAddress(address)
var (
existing = allocatedIPs[address.String()]
available = availableIPS[address.String()]
allocated = allocatedIPs[address.String()]
pos = getPosition(address, ip)
)
existing.Remove(int(pos))
available.Push(int(pos))
allocated.Remove(int(pos))
return nil
}
@ -82,29 +89,19 @@ func getPosition(address *net.IPNet, ip *net.IP) int32 {
func getNextIp(address *net.IPNet) (*net.IP, error) {
var (
ownIP = ipToInt(&address.IP)
available = availableIPS[address.String()]
allocated = allocatedIPs[address.String()]
first, _ = networkdriver.NetworkRange(address)
base = ipToInt(&first)
size = int(networkdriver.NetworkSize(address.Mask))
max = int32(size - 2) // size -1 for the broadcast address, -1 for the gateway address
pos = int32(available.Pop())
pos = atomic.LoadInt32(&allocated.last)
)
// We pop and push the position not the ip
if pos != 0 {
ip := intToIP(int32(base + pos))
allocated.Push(int(pos))
return ip, nil
}
var (
firstNetIP = address.IP.To4().Mask(address.Mask)
firstAsInt = ipToInt(&firstNetIP) + 1
)
pos = int32(allocated.PullBack())
for i := int32(0); i < max; i++ {
pos = pos%max + 1
next := int32(base + pos)
@ -116,6 +113,7 @@ func getNextIp(address *net.IPNet) (*net.IP, error) {
if !allocated.Exists(int(pos)) {
ip := intToIP(next)
allocated.Push(int(pos))
atomic.StoreInt32(&allocated.last, pos)
return ip, nil
}
}
@ -124,15 +122,14 @@ func getNextIp(address *net.IPNet) (*net.IP, error) {
func registerIP(address *net.IPNet, ip *net.IP) error {
var (
existing = allocatedIPs[address.String()]
available = availableIPS[address.String()]
allocated = allocatedIPs[address.String()]
pos = getPosition(address, ip)
)
if existing.Exists(int(pos)) {
if allocated.Exists(int(pos)) {
return ErrIPAlreadyAllocated
}
available.Remove(int(pos))
atomic.StoreInt32(&allocated.last, pos)
return nil
}
@ -153,7 +150,6 @@ func intToIP(n int32) *net.IP {
func checkAddress(address *net.IPNet) {
key := address.String()
if _, exists := allocatedIPs[key]; !exists {
allocatedIPs[key] = collections.NewOrderedIntSet()
availableIPS[key] = collections.NewOrderedIntSet()
allocatedIPs[key] = newAllocatedMap()
}
}

View file

@ -8,7 +8,6 @@ import (
func reset() {
allocatedIPs = networkSet{}
availableIPS = networkSet{}
}
func TestRequestNewIps(t *testing.T) {
@ -18,8 +17,10 @@ func TestRequestNewIps(t *testing.T) {
Mask: []byte{255, 255, 255, 0},
}
var ip *net.IP
var err error
for i := 2; i < 10; i++ {
ip, err := RequestIP(network, nil)
ip, err = RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
@ -28,6 +29,17 @@ func TestRequestNewIps(t *testing.T) {
t.Fatalf("Expected ip %s got %s", expected, ip.String())
}
}
value := intToIP(ipToInt(ip) + 1).String()
if err := ReleaseIP(network, ip); err != nil {
t.Fatal(err)
}
ip, err = RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
if ip.String() != value {
t.Fatalf("Expected to receive the next ip %s got %s", value, ip.String())
}
}
func TestReleaseIp(t *testing.T) {
@ -64,6 +76,17 @@ func TestGetReleasedIp(t *testing.T) {
t.Fatal(err)
}
for i := 0; i < 252; i++ {
_, err = RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
err = ReleaseIP(network, ip)
if err != nil {
t.Fatal(err)
}
}
ip, err = RequestIP(network, nil)
if err != nil {
t.Fatal(err)
@ -185,24 +208,6 @@ func TestIPAllocator(t *testing.T) {
newIPs[i] = ip
}
// Before loop begin
// 2(u) - 3(u) - 4(f) - 5(f) - 6(f)
// ↑
// After i = 0
// 2(u) - 3(u) - 4(f) - 5(u) - 6(f)
// ↑
// After i = 1
// 2(u) - 3(u) - 4(f) - 5(u) - 6(u)
// ↑
// After i = 2
// 2(u) - 3(u) - 4(u) - 5(u) - 6(u)
// ↑
// Reordered these because the new set will always return the
// lowest ips first and not in the order that they were released
assertIPEquals(t, &expectedIPs[2], newIPs[0])
assertIPEquals(t, &expectedIPs[3], newIPs[1])
assertIPEquals(t, &expectedIPs[4], newIPs[2])
@ -234,6 +239,86 @@ func TestAllocateFirstIP(t *testing.T) {
}
}
func TestAllocateAllIps(t *testing.T) {
defer reset()
network := &net.IPNet{
IP: []byte{192, 168, 0, 1},
Mask: []byte{255, 255, 255, 0},
}
var (
current, first *net.IP
err error
isFirst = true
)
for err == nil {
current, err = RequestIP(network, nil)
if isFirst {
first = current
isFirst = false
}
}
if err != ErrNoAvailableIPs {
t.Fatal(err)
}
if _, err := RequestIP(network, nil); err != ErrNoAvailableIPs {
t.Fatal(err)
}
if err := ReleaseIP(network, first); err != nil {
t.Fatal(err)
}
again, err := RequestIP(network, nil)
if err != nil {
t.Fatal(err)
}
assertIPEquals(t, first, again)
}
func TestAllocateDifferentSubnets(t *testing.T) {
defer reset()
network1 := &net.IPNet{
IP: []byte{192, 168, 0, 1},
Mask: []byte{255, 255, 255, 0},
}
network2 := &net.IPNet{
IP: []byte{127, 0, 0, 1},
Mask: []byte{255, 255, 255, 0},
}
expectedIPs := []net.IP{
0: net.IPv4(192, 168, 0, 2),
1: net.IPv4(192, 168, 0, 3),
2: net.IPv4(127, 0, 0, 2),
3: net.IPv4(127, 0, 0, 3),
}
ip11, err := RequestIP(network1, nil)
if err != nil {
t.Fatal(err)
}
ip12, err := RequestIP(network1, nil)
if err != nil {
t.Fatal(err)
}
ip21, err := RequestIP(network2, nil)
if err != nil {
t.Fatal(err)
}
ip22, err := RequestIP(network2, nil)
if err != nil {
t.Fatal(err)
}
assertIPEquals(t, &expectedIPs[0], ip11)
assertIPEquals(t, &expectedIPs[1], ip12)
assertIPEquals(t, &expectedIPs[2], ip21)
assertIPEquals(t, &expectedIPs[3], ip22)
}
func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) {
if !ip1.Equal(*ip2) {
t.Fatalf("Expected IP %s, got %s", ip1, ip2)