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

More ipallocator refactoring

Now x1.5 faster

Signed-off-by: Alexandr Morozov <lk4d4math@gmail.com>
This commit is contained in:
Alexandr Morozov 2014-05-28 22:10:09 +04:00
parent 6eaac7d571
commit 42dd48315f
No known key found for this signature in database
GPG key ID: 59BF89FA47378873
3 changed files with 58 additions and 71 deletions

View file

@ -317,14 +317,14 @@ func createBridgeIface(name string) error {
// Allocate a network interface // Allocate a network interface
func Allocate(job *engine.Job) engine.Status { func Allocate(job *engine.Job) engine.Status {
var ( var (
ip *net.IP ip net.IP
err error err error
id = job.Args[0] id = job.Args[0]
requestedIP = net.ParseIP(job.Getenv("RequestedIP")) requestedIP = net.ParseIP(job.Getenv("RequestedIP"))
) )
if requestedIP != nil { if requestedIP != nil {
ip, err = ipallocator.RequestIP(bridgeNetwork, &requestedIP) ip, err = ipallocator.RequestIP(bridgeNetwork, requestedIP)
} else { } else {
ip, err = ipallocator.RequestIP(bridgeNetwork, nil) ip, err = ipallocator.RequestIP(bridgeNetwork, nil)
} }
@ -342,7 +342,7 @@ func Allocate(job *engine.Job) engine.Status {
out.SetInt("IPPrefixLen", size) out.SetInt("IPPrefixLen", size)
currentInterfaces.Set(id, &networkInterface{ currentInterfaces.Set(id, &networkInterface{
IP: *ip, IP: ip,
}) })
out.WriteTo(job.Stdout) out.WriteTo(job.Stdout)
@ -367,7 +367,7 @@ func Release(job *engine.Job) engine.Status {
} }
} }
if err := ipallocator.ReleaseIP(bridgeNetwork, &containerInterface.IP); err != nil { if err := ipallocator.ReleaseIP(bridgeNetwork, containerInterface.IP); err != nil {
log.Infof("Unable to release ip %s", err) log.Infof("Unable to release ip %s", err)
} }
return engine.StatusOK return engine.StatusOK

View file

@ -3,19 +3,30 @@ package ipallocator
import ( import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"github.com/docker/docker/daemon/networkdriver"
"net" "net"
"sync" "sync"
"github.com/dotcloud/docker/daemon/networkdriver"
) )
// allocatedMap is thread-unsafe set of allocated IP // allocatedMap is thread-unsafe set of allocated IP
type allocatedMap struct { type allocatedMap struct {
p map[int32]struct{} p map[uint32]struct{}
last int32 last uint32
begin uint32
end uint32
} }
func newAllocatedMap() *allocatedMap { func newAllocatedMap(network *net.IPNet) *allocatedMap {
return &allocatedMap{p: make(map[int32]struct{})} firstIP, lastIP := networkdriver.NetworkRange(network)
begin := ipToInt(firstIP) + 2
end := ipToInt(lastIP) - 1
return &allocatedMap{
p: make(map[uint32]struct{}),
begin: begin, // - network
end: end, // - broadcast
last: begin - 1, // so first allocated will be begin
}
} }
type networkSet map[string]*allocatedMap type networkSet map[string]*allocatedMap
@ -34,43 +45,36 @@ var (
// will return the next available ip if the ip provided is nil. If the // will return the next available ip if the ip provided is nil. If the
// ip provided is not nil it will validate that the provided ip is available // ip provided is not nil it will validate that the provided ip is available
// for use or return an error // for use or return an error
func RequestIP(network *net.IPNet, ip *net.IP) (*net.IP, error) { func RequestIP(network *net.IPNet, ip net.IP) (net.IP, error) {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
key := network.String() key := network.String()
allocated, ok := allocatedIPs[key] allocated, ok := allocatedIPs[key]
if !ok { if !ok {
allocated = newAllocatedMap() allocated = newAllocatedMap(network)
allocatedIPs[key] = allocated allocatedIPs[key] = allocated
} }
if ip == nil { if ip == nil {
return allocated.getNextIP(network) return allocated.getNextIP()
} }
return allocated.checkIP(network, ip) return allocated.checkIP(ip)
} }
// ReleaseIP adds the provided ip back into the pool of // ReleaseIP adds the provided ip back into the pool of
// available ips to be returned for use. // available ips to be returned for use.
func ReleaseIP(network *net.IPNet, ip *net.IP) error { func ReleaseIP(network *net.IPNet, ip net.IP) error {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
if allocated, exists := allocatedIPs[network.String()]; exists { if allocated, exists := allocatedIPs[network.String()]; exists {
pos := getPosition(network, ip) pos := ipToInt(ip)
delete(allocated.p, pos) delete(allocated.p, pos)
} }
return nil return nil
} }
// convert the ip into the position in the subnet. Only func (allocated *allocatedMap) checkIP(ip net.IP) (net.IP, error) {
// position are saved in the set pos := ipToInt(ip)
func getPosition(network *net.IPNet, ip *net.IP) int32 {
first, _ := networkdriver.NetworkRange(network)
return ipToInt(ip) - ipToInt(&first)
}
func (allocated *allocatedMap) checkIP(network *net.IPNet, ip *net.IP) (*net.IP, error) {
pos := getPosition(network, ip)
if _, ok := allocated.p[pos]; ok { if _, ok := allocated.p[pos]; ok {
return nil, ErrIPAlreadyAllocated return nil, ErrIPAlreadyAllocated
} }
@ -81,47 +85,30 @@ func (allocated *allocatedMap) checkIP(network *net.IPNet, ip *net.IP) (*net.IP,
// return an available ip if one is currently available. If not, // return an available ip if one is currently available. If not,
// return the next available ip for the nextwork // return the next available ip for the nextwork
func (allocated *allocatedMap) getNextIP(network *net.IPNet) (*net.IP, error) { func (allocated *allocatedMap) getNextIP() (net.IP, error) {
var ( for pos := allocated.last + 1; pos != allocated.last; pos++ {
ownIP = ipToInt(&network.IP) if pos > allocated.end {
first, _ = networkdriver.NetworkRange(network) pos = allocated.begin
base = ipToInt(&first)
size = int(networkdriver.NetworkSize(network.Mask))
max = int32(size - 2) // size -1 for the broadcast network, -1 for the gateway network
pos = allocated.last
)
var (
firstNetIP = network.IP.To4().Mask(network.Mask)
firstAsInt = ipToInt(&firstNetIP) + 1
)
for i := int32(0); i < max; i++ {
pos = pos%max + 1
next := int32(base + pos)
if next == ownIP || next == firstAsInt {
continue
} }
if _, ok := allocated.p[pos]; ok { if _, ok := allocated.p[pos]; ok {
continue continue
} }
allocated.p[pos] = struct{}{} allocated.p[pos] = struct{}{}
allocated.last = pos allocated.last = pos
return intToIP(next), nil return intToIP(pos), nil
} }
return nil, ErrNoAvailableIPs return nil, ErrNoAvailableIPs
} }
// Converts a 4 bytes IP into a 32 bit integer // Converts a 4 bytes IP into a 32 bit integer
func ipToInt(ip *net.IP) int32 { func ipToInt(ip net.IP) uint32 {
return int32(binary.BigEndian.Uint32(ip.To4())) return binary.BigEndian.Uint32(ip.To4())
} }
// Converts 32 bit integer into a 4 bytes IP address // Converts 32 bit integer into a 4 bytes IP address
func intToIP(n int32) *net.IP { func intToIP(n uint32) net.IP {
b := make([]byte, 4) b := make([]byte, 4)
binary.BigEndian.PutUint32(b, uint32(n)) binary.BigEndian.PutUint32(b, n)
ip := net.IP(b) ip := net.IP(b)
return &ip return ip
} }

View file

@ -17,7 +17,7 @@ func TestRequestNewIps(t *testing.T) {
Mask: []byte{255, 255, 255, 0}, Mask: []byte{255, 255, 255, 0},
} }
var ip *net.IP var ip net.IP
var err error var err error
for i := 2; i < 10; i++ { for i := 2; i < 10; i++ {
ip, err = RequestIP(network, nil) ip, err = RequestIP(network, nil)
@ -106,19 +106,19 @@ func TestRequesetSpecificIp(t *testing.T) {
ip := net.ParseIP("192.168.1.5") ip := net.ParseIP("192.168.1.5")
if _, err := RequestIP(network, &ip); err != nil { if _, err := RequestIP(network, ip); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestConversion(t *testing.T) { func TestConversion(t *testing.T) {
ip := net.ParseIP("127.0.0.1") ip := net.ParseIP("127.0.0.1")
i := ipToInt(&ip) i := ipToInt(ip)
if i == 0 { if i == 0 {
t.Fatal("converted to zero") t.Fatal("converted to zero")
} }
conv := intToIP(i) conv := intToIP(i)
if !ip.Equal(*conv) { if !ip.Equal(conv) {
t.Error(conv.String()) t.Error(conv.String())
} }
} }
@ -146,7 +146,7 @@ func TestIPAllocator(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
assertIPEquals(t, &expectedIPs[i], ip) assertIPEquals(t, expectedIPs[i], ip)
} }
// Before loop begin // Before loop begin
// 2(f) - 3(f) - 4(f) - 5(f) - 6(f) // 2(f) - 3(f) - 4(f) - 5(f) - 6(f)
@ -179,19 +179,19 @@ func TestIPAllocator(t *testing.T) {
} }
// Release some IPs in non-sequential order // Release some IPs in non-sequential order
if err := ReleaseIP(network, &expectedIPs[3]); err != nil { if err := ReleaseIP(network, expectedIPs[3]); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// 2(u) - 3(u) - 4(u) - 5(f) - 6(u) // 2(u) - 3(u) - 4(u) - 5(f) - 6(u)
// ↑ // ↑
if err := ReleaseIP(network, &expectedIPs[2]); err != nil { if err := ReleaseIP(network, expectedIPs[2]); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// 2(u) - 3(u) - 4(f) - 5(f) - 6(u) // 2(u) - 3(u) - 4(f) - 5(f) - 6(u)
// ↑ // ↑
if err := ReleaseIP(network, &expectedIPs[4]); err != nil { if err := ReleaseIP(network, expectedIPs[4]); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// 2(u) - 3(u) - 4(f) - 5(f) - 6(f) // 2(u) - 3(u) - 4(f) - 5(f) - 6(f)
@ -199,7 +199,7 @@ func TestIPAllocator(t *testing.T) {
// Make sure that IPs are reused in sequential order, starting // Make sure that IPs are reused in sequential order, starting
// with the first released IP // with the first released IP
newIPs := make([]*net.IP, 3) newIPs := make([]net.IP, 3)
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
ip, err := RequestIP(network, nil) ip, err := RequestIP(network, nil)
if err != nil { if err != nil {
@ -208,9 +208,9 @@ func TestIPAllocator(t *testing.T) {
newIPs[i] = ip newIPs[i] = ip
} }
assertIPEquals(t, &expectedIPs[2], newIPs[0]) assertIPEquals(t, expectedIPs[2], newIPs[0])
assertIPEquals(t, &expectedIPs[3], newIPs[1]) assertIPEquals(t, expectedIPs[3], newIPs[1])
assertIPEquals(t, &expectedIPs[4], newIPs[2]) assertIPEquals(t, expectedIPs[4], newIPs[2])
_, err = RequestIP(network, nil) _, err = RequestIP(network, nil)
if err == nil { if err == nil {
@ -226,7 +226,7 @@ func TestAllocateFirstIP(t *testing.T) {
} }
firstIP := network.IP.To4().Mask(network.Mask) firstIP := network.IP.To4().Mask(network.Mask)
first := ipToInt(&firstIP) + 1 first := ipToInt(firstIP) + 1
ip, err := RequestIP(network, nil) ip, err := RequestIP(network, nil)
if err != nil { if err != nil {
@ -247,7 +247,7 @@ func TestAllocateAllIps(t *testing.T) {
} }
var ( var (
current, first *net.IP current, first net.IP
err error err error
isFirst = true isFirst = true
) )
@ -313,14 +313,14 @@ func TestAllocateDifferentSubnets(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assertIPEquals(t, &expectedIPs[0], ip11) assertIPEquals(t, expectedIPs[0], ip11)
assertIPEquals(t, &expectedIPs[1], ip12) assertIPEquals(t, expectedIPs[1], ip12)
assertIPEquals(t, &expectedIPs[2], ip21) assertIPEquals(t, expectedIPs[2], ip21)
assertIPEquals(t, &expectedIPs[3], ip22) assertIPEquals(t, expectedIPs[3], ip22)
} }
func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) { func assertIPEquals(t *testing.T, ip1, ip2 net.IP) {
if !ip1.Equal(*ip2) { if !ip1.Equal(ip2) {
t.Fatalf("Expected IP %s, got %s", ip1, ip2) t.Fatalf("Expected IP %s, got %s", ip1, ip2)
} }
} }