Merge pull request #5937 from LK4D4/ipallocator_refactoring

Ip allocator refactoring
This commit is contained in:
Michael Crosby 2014-05-27 12:22:36 -07:00
commit bdb5aa4c27
2 changed files with 60 additions and 71 deletions

View file

@ -4,19 +4,18 @@ import (
"encoding/binary"
"errors"
"github.com/dotcloud/docker/daemon/networkdriver"
"github.com/dotcloud/docker/pkg/collections"
"net"
"sync"
"sync/atomic"
)
// allocatedMap is thread-unsafe set of allocated IP
type allocatedMap struct {
*collections.OrderedIntSet
p map[int32]struct{}
last int32
}
func newAllocatedMap() *allocatedMap {
return &allocatedMap{OrderedIntSet: collections.NewOrderedIntSet()}
return &allocatedMap{p: make(map[int32]struct{})}
}
type networkSet map[string]*allocatedMap
@ -35,70 +34,65 @@ var (
// 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
// for use or return an error
func RequestIP(address *net.IPNet, ip *net.IP) (*net.IP, error) {
func RequestIP(network *net.IPNet, ip *net.IP) (*net.IP, error) {
lock.Lock()
defer lock.Unlock()
checkAddress(address)
key := network.String()
allocated, ok := allocatedIPs[key]
if !ok {
allocated = newAllocatedMap()
allocatedIPs[key] = allocated
}
if ip == nil {
next, err := getNextIp(address)
if err != nil {
return nil, err
}
return next, nil
return allocated.getNextIP(network)
}
if err := registerIP(address, ip); err != nil {
return nil, err
}
return ip, nil
return allocated.checkIP(network, ip)
}
// ReleaseIP adds the provided ip back into the pool of
// available ips to be returned for use.
func ReleaseIP(address *net.IPNet, ip *net.IP) error {
func ReleaseIP(network *net.IPNet, ip *net.IP) error {
lock.Lock()
defer lock.Unlock()
checkAddress(address)
var (
allocated = allocatedIPs[address.String()]
pos = getPosition(address, ip)
)
allocated.Remove(int(pos))
if allocated, exists := allocatedIPs[network.String()]; exists {
pos := getPosition(network, ip)
delete(allocated.p, pos)
}
return nil
}
// convert the ip into the position in the subnet. Only
// position are saved in the set
func getPosition(address *net.IPNet, ip *net.IP) int32 {
var (
first, _ = networkdriver.NetworkRange(address)
base = ipToInt(&first)
i = ipToInt(ip)
)
return i - base
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 {
return nil, ErrIPAlreadyAllocated
}
allocated.p[pos] = struct{}{}
allocated.last = pos
return ip, nil
}
// return an available ip if one is currently available. If not,
// return the next available ip for the nextwork
func getNextIp(address *net.IPNet) (*net.IP, error) {
func (allocated *allocatedMap) getNextIP(network *net.IPNet) (*net.IP, error) {
var (
ownIP = ipToInt(&address.IP)
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 = atomic.LoadInt32(&allocated.last)
ownIP = ipToInt(&network.IP)
first, _ = networkdriver.NetworkRange(network)
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 = address.IP.To4().Mask(address.Mask)
firstNetIP = network.IP.To4().Mask(network.Mask)
firstAsInt = ipToInt(&firstNetIP) + 1
)
@ -109,31 +103,16 @@ func getNextIp(address *net.IPNet) (*net.IP, error) {
if next == ownIP || next == firstAsInt {
continue
}
if !allocated.Exists(int(pos)) {
ip := intToIP(next)
allocated.Push(int(pos))
atomic.StoreInt32(&allocated.last, pos)
return ip, nil
if _, ok := allocated.p[pos]; ok {
continue
}
allocated.p[pos] = struct{}{}
allocated.last = pos
return intToIP(next), nil
}
return nil, ErrNoAvailableIPs
}
func registerIP(address *net.IPNet, ip *net.IP) error {
var (
allocated = allocatedIPs[address.String()]
pos = getPosition(address, ip)
)
if allocated.Exists(int(pos)) {
return ErrIPAlreadyAllocated
}
atomic.StoreInt32(&allocated.last, pos)
return nil
}
// Converts a 4 bytes IP into a 32 bit integer
func ipToInt(ip *net.IP) int32 {
return int32(binary.BigEndian.Uint32(ip.To4()))
@ -146,10 +125,3 @@ func intToIP(n int32) *net.IP {
ip := net.IP(b)
return &ip
}
func checkAddress(address *net.IPNet) {
key := address.String()
if _, exists := allocatedIPs[key]; !exists {
allocatedIPs[key] = newAllocatedMap()
}
}

View file

@ -324,3 +324,20 @@ func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) {
t.Fatalf("Expected IP %s, got %s", ip1, ip2)
}
}
func BenchmarkRequestIP(b *testing.B) {
network := &net.IPNet{
IP: []byte{192, 168, 0, 1},
Mask: []byte{255, 255, 255, 0},
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for j := 0; j < 253; j++ {
_, err := RequestIP(network, nil)
if err != nil {
b.Fatal(err)
}
}
reset()
}
}